前言
100岁生日快乐!!!
参考链接:
- https://medium.com/swlh/diving-into-unserialize-3586c1ec97e
- https://paragonie.com/blog/2016/04/securely-implementing-de-serialization-in-php
正文
PHP的反序列化函数unserialize()
接受两个参数
- 序列化字符串
- 选项
对于PHP开发者来说,选项只有一个,要么不设置,设置的话就只能是allowed_classes
定义了allowed_classes
选项之后,只有被PHP允许的类才能够被反序列化,否则会被初始化为__PHP_Incomplete_Class
反序列化流程:
也就是说,只要我们的反序列化类实现了__wakeup
或者__destruct
中的任何一个方法,只要serialized
方法反序列化了这样的对象,这两个方法就会被执行
反序列化一个对象需要满足一个前提
- 这个类一定是被预先定义过的
如果不满足这个前提,那么反序列化出来的类就是会被初始化为__PHP_Incomplete_Class
,但是其类名和成员变量还是可以正常反序列化出来的,只是PHP认为他是一个没有被定义的类
php的serialized
方法会保存被序列化的对象的所有属性(成员变量),有了这个,unserialize
就能创建出一个原来的对象的copy
copy创建出来之后,PHP会搜索该对象的__wakeup
方法,如果找到了该方法,就会执行其中的代码,该方法一般用来恢复数据库连接,因为序列化的对象在传输过程中可能已经和数据库失去了连接,需要在该方法中编写代码来重新初始化连接
然后就是对对象进行操作(调用对象的方法),最后销毁对象(调用__destruct
方法)
一旦攻击者控制类传递给unserialize
方法的序列化字符串,那么攻击者就能够控制对象的属性(成员变量)
能够随心所欲的操作成员变量,就有可能控制程序执行的流程,进而达到RCE
利用反序列化漏洞需要两个前提:
- 反序列化的类一定要被预先定义
- 该类一定要拥有魔术方法
__wakeup
或者__destruct
,不管最终存不存在漏洞,如果没有这两个方法,你连注入代码的机会都没有
例子
假如有下面这样一段代码:
<?php
class Fuck
{
private $test;
function __destruct()
{
system($this->test);
}
}
$obj = unserialize(base64_decode(此处接收用户输入));
那么我们对应的exp就是:
<?php
class Fuck
{
private $test = "hostname";
}
print(base64_encode(serialize(new Fuck)));
预防反序列化漏洞
限制反序列化或者白名单
//不允许反序列化对象(初始化出一个__PHP_Incomplete_Class对象),但是你仍然可以反序列化其他的数据结构,比如数组等
$object = unserialize($data, ['allowed_classes' => false]);
//白名单,只能反序列化白名单中的类
$whitelist = ['MyProject\\OtherNamespace\\ObjectAllowed'];
$object = unserialize($data, ['allowed_classes' => $whitelist]);
建议不要将用户输入用于反序列化