该漏洞是Mail2000的Web服务在处理多个文件的http数据包时未对全局结构体中的数组进行边界检查,导致越界写堆地址。针对原作者使用堆喷+爆破的利用手法,本文将介绍一种仅爆破地址即可利用的更加稳定的手法。
Mail2000的Web服务器采用的是Apache httpd和CGI (Common Gateway Interface)的架构,实现如图:
图片来源:参考链接[1]
libm2k.so
和libm2kc.so
是由Openfind开发实现的两个核心库。本文分析的漏洞就存在libm2kc中。Content-Type
指定是multipart
以及指定boundary
,http正文中每两个boundary
之间表示一个文件内容。文件内容是http头加上http正文的格式。在处理多个文件的请求时,libm2kc
使用stCGIEnv
结构体来存储相关文件信息。这里重点关注multipart_file_arr
变量,该变量是MultipartFileVar
结构体数组,个数固定为200。MultipartFileVar
中的name
和filaname
对应文件内容中的Content-Disposition
中的name
和filename
。
libm2kc
中首先跟据boundary
找到每个文件内容,之后在解析Content-Disposition
的name
和filename
,存放到对应的MultipartFileVar
结构体,一个文件对应一个MultipartFileVar
结构体。
漏洞点在于在使用stCGIEnv.file_cur_count
作为index获取stCGIEnv.multipart_file_arr
数组元素时并没有检查个数是否大于200。当我们发送的http请求中的文件个数超过200时会导致越界写。因为name
和filename
是堆地址所以实际上是越界写堆地址。
覆盖函数指针
最先想到的是MFCGI_ReaderCB
变量,这是一个函数指针,功能是在CGI初始化时读取http请求数据。因为越界写的是堆地址,而程序又开启了NX导致堆不可执行,所以该思路无法利用。
覆盖含有_IO_FILE结构体
接下来看正好在溢出数组的下方的三个变量
前面提到MultipartFileVar
存的是Content-Disposition
的内容,除此之外,Content-Type
,文件内容等会存放在file_arr
指向的结构体数组。
file_arr
指向的结构体数组:
FILE
结构体,很容易想到ctf中_IO_FILE
的利用。_IO_FILE
的利用原理是glibc中一些操作文件的函数会调用FILE结构体中的vtable
中的函数。fclose
的部分源码,12行的_IO_FINISH
展开实际上是调用fp
的vtable
中的函数。在漏洞本身中的利用链如下:
程序开启了PIE和ASLR,按照ctf的常规思路是:泄露堆和libc地址,进行堆布局伪造相关架构体,最后劫持执行流。
实际泄露地址没有意义。
前文提到,该web服务器架构是,当发送一个http请求时,httpd fork子进程,运行CGI程序处理请求。
假设此时我们获得堆和libc地址,计算好实际地址后,再次发送请求。再次发送请求时,http又会重新fork一个进程处理请求,此时的子进程与处理上一个请求的进程是不同的进程,所以泄露出的地址就没有意义。
那么如何绕过ASLR?
32位
首先讨论32位下的利用。
在32位程序中,即使开启了ASLR,地址也只有12位是随机的。理论上有1/4096的概率爆破成功。所以通过爆破我们能够获得堆或libc的地址。
思路一:堆喷POSTfile、_IO_FILE等结构体来绕过ASLR,再修改vtable中的函数为libc中的函数(如system),libc中的函数地址需要通过爆破获得。这也是原作者的做法。
思路二:因为最后劫持执行流时需要无论如何都需要爆破libc地址,所以可以把需要伪造的结构体放到libc的正上方。
glibc中的malloc申请超过128k的内存时会使用mmap分配内存(而不是brk的内存)。mmap分配的内存会正好在libc的上方。单个请求包大小最大是128M,可以满足128k的条件。
我们可以把要伪造的结构体放进文件内容中,在解析文件时会分配空间从而将伪造的数据写到libc的正上方。
通过这种方式,伪造结构体所需要的地址和vtable函数的地址全部可以由libc地址加上一段偏移计算得出。
在成功劫持程序执行流后,此时的函数调用是func(fp)
。此时eax指向_IO_FILE_plus
,通过下方第一个gadget将esp指向_IO_FILE_plus
内部,在通过第二个gadget使esp
指向gadget。之后就是利用ROP完成RCE。
此外httpd会将一些变量通过环境变量传递给cgi程序,比如HTTP_HOST
、REQUEST_METHOD
、QUERY_STRING
。
例如我们发送数据包POST /url?shellcode
,shellcode
会通过QUERY_STRING=shellcode
这个环境变量存放在cgi程序栈上。通过这种方式可以把shellcode布置到栈上。
之后类似第一种做法,堆喷POSTfile
、_IO_FILE
结构体,爆破stack
地址把vtable
中的函数指针指向存放shellcode的栈地址。
POSTfile
、_IO_FILE
结构体,但是劫持执行流时必须要用到libc的地址,所以无法利用。一般而言,类似Mail2000的web服务器的架构的内存类漏洞利用难度较大,因为一个请求对应一个进程,这要求我们一个请求就完成利用。本文分析的漏洞正好能够控制_IO_FILE
结构体,从而通过_IO_FILE
的利用实现RCE,而且因为程序开启了ASLR,利用也只能在32位下完成。
【版权说明】
本作品著作权归noir所有
未经作者同意,不得转载
天工实验室安全研究员
研究领域:物联网安全
原文始发于微信公众号(破壳平台):Openfind Mail2000 认证前RCE漏洞分析
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论