Phar反序列化及其Bypass

温馨提示:点击页面下方以展开或折叠目录~

Phar反序列化

引子:反序列化漏洞一般要借助unserialize()函数,不过在Blackhat2018,出现了一种可以不借助unserialize()函数而触发php反序列化漏洞的方法

论文、blackhat PPT

博客参考

注:复现最好在8.0之前的版本做(不是最好,是一定,,因为php 8.0后phar://触发不了反序列化了)

Phar简介

Phar:参考php手册 (吐槽一下这个中文版手册除了简介两个字是中文愣是没有一句话是翻译出来的)

The phar extension provides a way to put entire PHP applications into a single file called a “phar” (PHP Archive) for easy distribution and installation.

——php手册

还不太懂?对比下jar和phar的维基解释:

A JAR (Java ARchive) is a package file format typically used to aggregate many Java class files and associated metadata and resources (text, images, etc.) into one file for distribution.

——wiki)

In software, a PHAR (PHP Archive) file is a package format to enable distribution of applications and libraries by bundling many PHP code files and other resources (e.g. images, stylesheets, etc.) into a single archive file.

——wiki)

简而言之,phar就是像java中的jar一样,是一种可以将整个php应用打包以便于部署的打包文件(本质是个压缩文件)

文件结构

image-20211203214714452

  1. Stub(参考php手册

    A PHP file that will bootstrap the archive. The stub must contain the __HALT_COMPILER(); token, and the default stub includes the ability to run a PHAR with or without the PHP extension enabled。

    __HALT_COMPILER();结尾的标志,令php扩展可以将其识别为phar文件

    php手册中说可以不要?>,不过在生成phar包的时候好像会自动带上?>

  2. Manifest

    The manifest details the contents of the archive.

    在phar中的每个文件的属性等信息都在其中,关键点在于用户自定义的meta-data会以serialize()的方式存在这里面(这也是攻击的核心,主要就是利用解析phar时对meta-data的反序列化)

    image-20211203212226864

  3. Contents

    The original files that are included in the archive.

    被压缩的文件内容

  4. Signature

    签名,在文件末尾

image-20211203214753399

010下打开phar包,可以清晰的看到stub的标志、meta-data的序列化内容

image-20211207111826559

图片来源:BlackHat演讲ppt

Phar生成与打包

生成phar时若报错如下

1
creating archive "xxx.phar" disabled by the php.ini setting phar.readonly

需要修改php.ini配置,把phar.readonly=on去掉注释,然后再改为off

1
2
3
[Phar]
; http://php.net/phar.readonly
phar.readonly = Off
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class A{
public $a;
public function __construct(){
$this->a = "command";
}
}

@unlink("myTest.phar");
$phar = new Phar("myTest.phar");
$phar -> startBuffering();
$phar -> setStub("__HALT_COMPILER();"); // 设置Stub
$o = new A();
$phar -> setMetadata($o); // 将自定义的meta-data存入manifest
$phar -> addFromString("1.txt", "test"); // 添加要压缩的文件
$phar -> stopBuffering();

生成的myTest.phar目录结构如下

image-20211203214932885

此时查看myTest.phar的二进制文件,可以发现命令以明文方式存在,如果遇到过滤则无法通过

image-20211203214954198

漏洞原理

漏洞触发点在使用phar://协议读取文件的时候,文件内容会被解析成phar对象,然后phar对象内的Metadata信息会被反序列化。

参考开头放过了….写这个的间隔太久了都忘了,,

触发函数

大部分文件操作的函数都可以触发

image-20211204094859918

来源:https://paper.seebug.org/680/

不过其实不止上面这些函数可以触发,在 https://blog.zsxsoft.com/post/38 这篇文章里,详细描述了phar反序列化漏洞的触发机制,以及其他一些可用函数和奇淫技巧

Demo测试:

image-20211204095024605

利用条件

  1. phar文件能够上传
  2. 有可用的魔术方法作为跳板
  3. 文件操作函数的参数可控

Bypass

压缩文件绕过

  • 若题目对HALT_COMPILER()及各类命令执行语句进行过滤

新建文件夹.phar,在文件夹下新建.metadata文件,写入序列化数据O:1:"A":1:{s:1:"a";s:6:"TEST!!";},将.phar文件夹压缩为1.tar.gz如下,发现已经不存在明文(1.tar时还是存在明文的)

image-20211204203342690

使用phar://1.tar.gz即可读取并触发反序列化

image-20211204211256017

头部字符串禁phar绕过

压缩过滤器触发phar时解决phar:// 不能出现在首部

1
2
3
4
5
compress.bzip://phar://...
compress.bzip2://phar://...
compress.zlib://phar://...
php://filter/resource=phar://....
php://filter/read=convert.base64-encode/resource=phar://phar.phar

这里测试时要注意,php默认不开启bzip2(bz2)支持,需要在php.ini 867行(可能有差)将extension=php_bz2.dll的注释取消

否则会报Unable to find the wrapper "compress.bzip2"

并且在php.ini 725行(可能有差)将extension_dir修改为自己的ext路径(我的是extension_dir = "D:/DevelopTools/php-7.0.0/ext",然后记得要用斜杠而不是反斜杠)

否则会报PHP Startup: Unable to load dynamic library(好像ext不设置的话默认路径是C:/php/ext(windows))

image-20211205100306539

经测试均能触发反序列化

Gif绕过

由于php中判断一个文件是不是phar是根据有没有__HALT_COMPILER();来判断的,而不管其前后,因此在文件头写入GIF的文件头,可以让文件被识别为gif的同时,也能被phar://解析

在生成phar时,通过setStub在文件头部添加GIF89a的gif头部标志,生成后再修改后缀名为gif

$phar->setStub(“GIF89a”."<?php __HALT_COMPILER(); ?>");

image-20211205102302041

先生成一个gifTest.phar后修改后缀为gifTest.gif,再用phar://去读,发现仍然可以反序列化,并且获取文件信息发现是GIF,可以绕过文件类型检测

获取文件类型的finfo函数也需要在php.ini内注释掉extension=php_fileinfo.dll来支持扩展