PHAR漏洞详解
PHAR(PHP Archive)文件格式 是PHP内置的归档文件格式,类似于ZIP
、TAR
等,它可以将多个文件打包成一个文件进行处理。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 |
|
- 该部分表示 PHAR 文件的“入口”,
Phar::mapPhar()
用来加载 PHAR 文件,并使用include
引入其中的文件。 - 绕过上传限制:由于
Stub
部分可以是任意 PHP 代码,只要最后以__HALT_COMPILER();
结束,因此攻击者可以构造一个带有这个标志的文件(例如伪造为图片格式),从而绕过文件上传限制(例如上传过滤器只允许图片文件)。只要文件中包含合法的Stub
结尾,PHP 解释器就会将它识别为 PHAR 文件。
2. Manifest(文件内容的描述)
Manifest
是 PHAR 文件的核心元数据部分,它描述了 PHAR 文件内部包含的所有文件的信息。这包括:
- 每个文件的名称、大小、时间戳、文件权限、校验和等属性。
- 还包括序列化的用户自定义
meta-data
,这是 PHAR 漏洞利用的关键部分。
Manifest 中的内容:
- 文件信息:每个文件的信息,包括文件名、压缩方式、文件大小等属性都会记录在
Manifest
部分。 - 元数据(Meta-data):这一部分是存储用户自定义的序列化数据,也就是攻击者可以利用的部分。如果攻击者可以通过某种方式注入恶意的 PHP 对象到这个元数据中,当 PHAR 文件被加载时,这些恶意对象会被反序列化并执行攻击代码。
序列化对象漏洞的利用点: PHAR 文件可以包含序列化的 PHP 对象。如果一个应用程序存在文件包含漏洞(例如本地文件包含,LFI),并且通过 phar://
协议来加载文件时,PHP 会自动反序列化这些元数据中的对象。如果这些对象包含恶意代码(例如有不安全的 __destruct()
或 __wakeup()
方法),就会触发远程代码执行。
3. 文件内容(File Contents)
这一部分是 PHAR 文件实际存储的文件数据。被压缩的文件会被分配到 Manifest
中定义的位置,并且可以通过 phar://
协议进行访问。
例如,如果 PHAR 文件中有一个名为
index.php
的文件,应用程序可以使用以下方式访问它:1
include 'phar://example.phar/index.php';
文件内容可以是 PHP 文件、图片、文本文件等任意格式。
4. 可选的签名(Signature)
签名是 PHAR 文件的完整性校验部分,通常位于文件末尾。签名用于验证 PHAR 文件在分发过程中是否被篡改。PHAR 文件的签名支持多种算法,包括:
- SHA-1
- SHA-256
- SHA-512
- MD5
签名的作用:
- 验证完整性:签名保证了 PHAR 文件未被篡改。如果签名不正确,PHAR 文件可能会被拒绝加载(取决于 PHP 配置)。
- 签名机制是可选的,某些情况下签名可能被禁用或忽略。
注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
demo
1 |
|
伪造文件头
可以 phar 文件必须以__HALT_COMPILER();?>
来结尾,那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。因此假设这里我们构造一个带有图片文件头部的 phar 文件。
例子
1 |
|
注意要同版本
如果题目限制了,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.