PHP反序列化
在理解这个漏洞前,你需要先搞清楚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
序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题
漏洞举例:
class S{
var $test = "pikachu";
function __destruct(){
echo $this->test;
}
}
$s = $_GET['test'];
@$unser = unserialize($a);
payload:O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
常见的魔法函数
1、__wakeup()
__wakeup()是在反序列化操作中起作用的魔法函数,当unserialize的时候,会检查时候存在__wakeup()函数,如果存在的话,会优先调用__wakeup()函数。
2、__construct()
实例化对象时被调用。__construct()是构造函数,同时当函数名和类名相同的时候,也是构造函数,构造函数有两种表达方式。
但是当__construct和以类名为函数名的函数同时存在的时候,__construct将被调用,而以类名作为函数名的构造函数不被调用。
3、__destruct()
当删除一个对象或对象操作终止时被调用。
4、__call()
当对象调用某个方法的时候,若方法存在,则直接调用;若不存在,则会去调用__call函数。
5、__get()
读取一个对象的属性时,若属性存在,则直接返回属性值;若不存在,则会调用__get函数。
6、__set()
设置一个对象的属性时,若属性存在,则直接赋值;若不存在,则会调用__set函数。
7、__toString()
打印一个对象的时被调用。如 echo $obj ,或 print $obj;
8、__clone()
克隆对象时被调用。如:$t=new Test(),$t1=clone $t;
9、__sleep()
serialize之前被调用。若对象比较大,想删减一点东东再序列化,可考虑一下此函数。
10、__isset()
检测一个对象的属性是否存在时被调用。如:isset($c->name)。
11、__unset()
unset一个对象的属性时被调用。如:unset($c->name)。
12、__set_state()
调用var_export时,被调用。用__set_state的返回值做为var_export的返回值。
13、__autoload()
实例化一个对象时,如果对应的类不存在,则该方法被调用。
关于php魔法函数爆出的安全问题,其实很多都是他们之间的一个组合的过程中忽略了它们之间的关系而造成的绕过,从而形成的安全问题。