来填坑了,差点就忘了,今天做题遇见了才想起来的
什么是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
压缩后同样也可以进行反序列化
还有签名计算那个,暂时还没弄