February 26, 2024

Phar反序列化漏洞详解

PHAR漏洞详解

PHAR(PHP Archive)文件格式 是PHP内置的归档文件格式,类似于ZIPTAR等,它可以将多个文件打包成一个文件进行处理。PHAR文件的设计目的是为了简化分发和使用PHP程序。然而,由于PHAR文件包含了序列化数据,它也容易受到和PHP反序列化相关的漏洞攻击,例如反序列化漏洞文件包含漏洞

PHAR漏洞一般涉及到反序列化攻击文件包含漏洞的结合,具体表现为通过文件包含函数读取PHAR文件中的序列化数据,从而引发反序列化攻击

PHAR文件的工作原理

PHAR文件内部由多个部分组成,包括文件内容和元数据。PHAR的元数据部分是一个可被序列化的PHP对象。因此,PHAR文件本质上包含了一个序列化的PHP对象,当这个PHAR文件被读取或操作时,PHP会自动将元数据反序列化。

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

PHAR 文件的大体结构可以分为 4 个部分:


1. Stub(文件标识)

Stub 是 PHAR 文件的标志,用来表明该文件是一个 PHAR 文件。这个部分通常是 PHP 代码,并且以 __HALT_COMPILER(); 结尾,表示停止 PHP 编译器的进一步执行。任何位于 __HALT_COMPILER() 之后的二进制数据都不会被当作 PHP 代码执行。

典型的 Stub 代码:

1
2
3
4
5
<?php
Phar::mapPhar();
include 'phar://phar.phar/index.php';
__HALT_COMPILER();
?>

2. Manifest(文件内容的描述)

Manifest 是 PHAR 文件的核心元数据部分,它描述了 PHAR 文件内部包含的所有文件的信息。这包括:

Manifest 中的内容:

序列化对象漏洞的利用点: PHAR 文件可以包含序列化的 PHP 对象。如果一个应用程序存在文件包含漏洞(例如本地文件包含,LFI),并且通过 phar:// 协议来加载文件时,PHP 会自动反序列化这些元数据中的对象。如果这些对象包含恶意代码(例如有不安全的 __destruct()__wakeup() 方法),就会触发远程代码执行。


3. 文件内容(File Contents)

这一部分是 PHAR 文件实际存储的文件数据。被压缩的文件会被分配到 Manifest 中定义的位置,并且可以通过 phar:// 协议进行访问。


4. 可选的签名(Signature)

签名是 PHAR 文件的完整性校验部分,通常位于文件末尾。签名用于验证 PHAR 文件在分发过程中是否被篡改。PHAR 文件的签名支持多种算法,包括:

签名的作用:

注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。

demo

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

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件,这里向 PHAR 文件中添加了一个名为 test.txt 的文件,文件内容为 "test"。这一步模拟了在 PHAR 文件中打包文件的过程。通常情况下,PHAR 文件可以包含任意格式的文件(如 PHP 文件、文本文件等)。
//签名自动计算
$phar->stopBuffering();
?>

image-20241019233515855

伪造文件头

可以 phar 文件必须以__HALT_COMPILER();?>来结尾,那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。因此假设这里我们构造一个带有图片文件头部的 phar 文件。

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php 
class Demo {

}
@unlink('demo_jpg.phar');
$phar = new Phar('demo_jpg.phar');
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$d = new Demo();
$d->data='haaaaa';
$phar->setMetadata($d);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();


<?php
include('phar://demp.phar');
class Demo {
function _destruct(){
echo $this->data;
}
}


注意要同版本

image-20241020000113300

如果题目限制了,phar://不能出现在头几个字符。可以用Bzip / Gzip协议绕过。

1
$filename = 'compress.zlib://phar://phar.phar/test.txt';

虽然会警告但仍会执行,它同样适用于compress.bzip2://
当文件系统函数的参数可控时,我们可以在不调用unserialize()的情况下进行反序列化操作,极大的拓展了反序列化攻击面。

About this Post

This post is written by void2eye, licensed under CC BY-NC 4.0.

#Web