在理解这个漏洞前,你需要先搞清楚php中serialize(),unserialize()这两个函数。
序列化serialize()
序列化说通俗点就是把一个对象变成可以传输的字符串,比如下面是一个对象:
class S{ public $test="pikachu"; } $s=new S(); //创建一个对象 serialize($s); //把这个对象进行序列化 序列化后得到的结果是这个样子的:O:1:"S":1:{s:4:"test";s:7:"pikachu";} O:代表object 1:代表对象名字长度为一个字符 S:对象的名称 1:代表对象里面有一个变量 s:数据类型 4:变量名称的长度 test:变量名称 s:数据类型 7:变量值的长度 pikachu:变量值
反序列化unserialize()
就是把被序列化的字符串还原为对象,然后在接下来的代码中继续使用。
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}"); echo $u->test; //得到的结果为pikachu
0x01 初识反序列化
反序列化我们需要了解php的类和魔术方法,这里举个简单的例子用到的魔术方法是__destruct
销毁一个类之前执行执行析构方法。
当对象创建后输出我们的$a
变量的值。那么我们把它的值改变后用serialize()
看看
可以看见这是我们12345序列化之后的值O:4:"test":1:{s:1:"a";s:5:"12345";}
那么我们传入O:4:"test":1:{s:1:"a";s:5:"zhong";}
O:4:"test":1:{s:1:"a";s:5:"12345";}
是我们序列化的值,然后unserialize($_GET['id']);
传入我们改变的值 O:4:"test":1:{s:1:"a";s:5:"zhong";}
成功打印了我们的改变的值,因为反序列化我们可以控制类属性且这个过程会触发这些能够触发的魔术方法
__wakeup() //使用unserialize时触发 __sleep() //使用serialize时触发 __destruct() //对象被销毁时触发 __call() //在对象上下文中调用不可访问的方法时触发 __callStatic() //在静态上下文中调用不可访问的方法时触发 __get() //用于从不可访问的属性读取数据 __set() //用于将数据写入不可访问的属性 __isset() //在不可访问的属性上调用isset()或empty()触发 __unset() //在不可访问的属性上使用unset()时触发 __toString() //把类当作字符串使用时触发 __invoke() //当脚本尝试将对象调用为函数时触发
0X02从一道简单的CTF了解
<?php class foo1{ public $varr; function __destruct(){ $this->varr->evaltest(); } } class foo2{ public $str; function evaltest(){ eval($this->str); } } ?>
我们看到在foo2中evaltest()
函数中存在eval,而foo1中调用了函数evaltest()
,我们就想可不可以让foo1调用foo2中的evaltest()
函数顺边还把它的$str
中的值改写了。
我们看到在foo2中evaltest()
函数中存在eval,而foo1中调用了函数evaltest()
,我们就想可不可以让foo1调用foo2中的evaltest()
函数顺边还把它的$str
中的值改写了