来填坑了,差点就忘了,今天做题遇见了才想起来的
什么是 Phar
Phar 是 PHP 的压缩文档,是 PHP 中类似于 JAR 的一种打包文件,他可以把多个文件存放到同一个文件中,无需解压,PHP 就可以进行访问并执行内部语句
同时对 PHP 版本有要求
PHP version >-5.3
Phar 文件的结构
大致分为四个部分
1、Stub //Phar文件头
2、manifest //压缩文件信息
3、contents //压缩文件内容
4、signature //签名
Sutb
其中 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
进行反序列化操作
利用条件:
1、phar文件能够上传至服务器
//即要求存在file_get_contents()、fopen()这种函数
2、要有可利用的魔术方法
//这个的话用一位大师傅的话说就是利用魔术方法作为"跳板"
3、文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤
//一般利用姿势是上传Phar文件后通过伪协议Phar来实现反序列化,伪协议Phar格式是`Phar://`这种,如果这几个特殊字符被过滤就无法实现反序列化
4、php.ini中的phar.readonly选项,需要为Off(默认是on)。
Phar 属于伪协议,伪协议使用较多的是一些文件操作函数,如 fopen()
、copy()
、file_exists()
等,具体如下图,也就是下面的函数如果参数可控可以造成 Phar 反序列化
生成 Phar 文件的必要流程,举个例子
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();//开启缓冲区
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
/*可选*/
$o = new TestObject();
$o -> data='hu3sky';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();//关闭缓冲区
?>
注:文件上传时,不必要.phar 后缀,文件上传不是难点!!(phar 伪协议自动解析成.phar 文件)
学都学了,那就一并整理一下绕过吧
比较重要的是,记得更改自己的 php.ini
将 phar.readonly 那一行设置为 Off,将前面的分号去掉,分号在这里起着注释符的作用
绕过方式
更改文件格式
利用 Phar 反序列化的第一步就是需要将 Phar 文件上传到服务器,若服务器存在防护,那就需要更改文件格式
比如只允许上传 gif 文件
PHP 通过 Stub
里的__HALT_COMPILER();
来识别这个文件是 Phar 文件,对于其他无限制。
故 对文件后缀、文件名进行更改,其实质仍然是 Phar 文件,所以不用担心文件后缀的问题
绕过正则 phar
if (preg_match("/^php|^file|^phar|^dict|^zip/i",$filename){
die();
}
可以通过各种协议绕过
1、使用filter伪协议来进行绕过
php://filter/read=convert.base64-encode/resource=phar://test.phar
2、使用bzip2协议来进行绕过
compress.bzip2://phar:///test.phar/test.txt
3、使用zlib协议进行绕过
compress.zlib://phar:///home/sx/test.phar/test.txt
绕过__HALT_COMPILER检测
可以通过前面加上图片头绕过
$phar->setStub("GIF89a<?php __HALT_COMPILER();?>");
也可将 Phar 文件的内容写到压缩包的注释中,压缩为 zip 文件
<?php
$a = serialize($a);
$zip = new ZipArchive();
$res = $zip->open('phar.zip',ZipArchive::CREATE);
$zip->addFromString('flag.txt', 'flag is here');
$zip->setArchiveComment($a);
$zip->close();
?>
或者是将生成的 Phar 文件进行 gzip 压缩,压缩命令
gzip test.phar
//gzip 文件名.phar
压缩后同样也可以进行反序列化
还有签名计算那个,暂时还没弄