PHP魔术函数__wakeup()漏洞分析

0x00.前言
由于本次我们学校办ctf,团队里有人出了跟这个漏洞相关的题,所以做的时候跟着去分析了一下。
0x01.分析
漏洞影响版本:

PHP5 < 5.6.25 PHP7 < 7.0.10

官方给出的bug分析链接为:
https://bugs.php.net/bug.php?id=72663

漏洞原理:
当一个对象被序列化的时候,PHP会调用__sleep方法(如果存在的话). 在反序列化一个对象后,PHP 会调用__wakeup方法. 这两个方法都不接受参数. 但__sleep方法必须返回一个数组,包含需要序列化的属性. PHP会抛弃其它属性的值. 如果没有__sleep方法,PHP将保存所有属性.

所以当一个对象非常大的时候,我们又不需要保存它所有的属性信息时,就可以用到__sleep,这样在序列化之后就只会保留此方法返回数组里的属性的值。而__wakeup的作用是在反序列化时,首先调用,用来重建对象可能具有的任何资源。也就是说可以用来找回你在序列化中__sleep方法中丢弃的属性。

这个漏洞就发生在反序列化的过程中,当我们反序列化一个对象时,会首先调用__wakeup方法,但是当反序列话属性错误的时候,就不会执行__wakeup,但是仍然会调用__destruct,那么如果__wakeup中有一些重要的语句什么的就不会被执行,比如反序列化对象的安全检测这些,结果就会造成安全隐患。
本地测试代码如下:

<?php
class test
{
    public $infomation = array();
    public $sex = '';
    public function __construct($infomation,$sex)
    {
        $this->infomation = $infomation;
        $this->sex = $sex;
    }
    public function __destruct()
    {
       echo "destruct ok\n";
       var_dump($this);
    }
    public function __wakeup()
    {
        echo "wakeup ok\n";
        $this->sex = 'woman';
    }
}
//$a = new test(array('0'=>i,'1'=>am,'2'=>matcha),'man');
//echo serialize($a);
$b = 'O:4:"test":2:{s:10:"infomation";a:3:{i:0;s:1:"i";i:1;s:2:"am";i:2;s:6:"matcha";}s:3:"sex";s:3:"man";}';
$c = unserialize($b);
//echo $c[1];
?>

当传入正确属性的反序列化的对象时,__wakeup被调用,可以看到反序列对象的sex属性被修改为了”woman”;
正常反序列对象:

O:4:"test":2:{s:10:"infomation";a:3:{i:0;s:1:"i";i:1;s:2:"am";i:2;s:6:"matcha";}s:3:"sex";s:3:"man";}

但是当我们修改了反序列对象的属性时,__wakeup没有被调用,跳过了,然而__destruct依然正常调用,反序列化对象的sex属性依然是”man”;
修改后的反序列对象:

O:4:"test":3:{s:10:"infomation";a:3:{i:0;s:1:"i";i:1;s:2:"am";i:2;s:6:"matcha";}s:3:"sex";s:3:"man";}


这样的话,如果__wakeup中是一些重要的检测,或者是其他重要的操作,当传入错误的序列化对象时就会被绕过执行,对安全的隐患还是很大的。

发表评论

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