
D-Link_DIR-859RCE漏洞CVE-2019-17621复现
漏洞描述
D-Link DIR-859 Wi-Fi路由器的1.05与1.06B01 Beta01版固件中UPnP的gena.cgi,允许未经身份验证的远程攻击者,在连接到本地网络时,通过向UPnP发送特定的HTTP SUBSCRIBE请求,从而实现以root身份执行系统命令
固件下载
下载地址
固件模拟
网络上都是用fat模拟的,我还是直接用qemu虚拟机模拟
主机:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #!/bin/bash
sudo tunctl -t tap0
sudo ip addr add 192.168.0.4/24 dev tap0 sudo ip link set tap0 up
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null
sudo iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE
sudo iptables -A FORWARD -i tap0 -j ACCEPT sudo iptables -A FORWARD -o tap0 -j ACCEPT
echo "tap0 接口创建和配置完成。"
|
qemu:
1 2 3
| root@debian-mips:~# ip link add br0 type dummy root@debian-mips:~# ifconfig eth0 192.168.0.5/24 root@debian-mips:~# ifconfig br0 192.168.0.6/24
|
文件系统的话在本地开个python端口用wget即可
之后用firmwalker
跑出来服务是/sbin/httpd
image-20250330162733537分析下httpd
image-20250330163011409发现确实打开这个了,但是系统中没有这个。
漏洞分析
根据漏洞描述逆向分析cgibin
image-20250330164130876
image-20250330165635751跟进xmldbc_ephp
函数,套了好几层,主要是发送了前面的报文
image-20250330165815319并且分析报文发现传入了run.NOTIFY.php
,去查看下该文件
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
| <? include "/htdocs/phplib/upnp/xnode.php"; include "/htdocs/upnpinc/gvar.php"; include "/htdocs/upnpinc/gena.php";
$gena_path = XNODE_getpathbytarget($G_GENA_NODEBASE, "inf", "uid", $INF_UID, 1); $gena_path = $gena_path."/".$SERVICE; GENA_subscribe_cleanup($gena_path);
if ($SERVICE == "L3Forwarding1") $php = "NOTIFY.Layer3Forwarding.1.php"; else if ($SERVICE == "OSInfo1") $php = "NOTIFY.OSInfo.1.php"; else if ($SERVICE == "WANCommonIFC1") $php = "NOTIFY.WANCommonInterfaceConfig.1.php"; else if ($SERVICE == "WANEthLinkC1") $php = "NOTIFY.WANEthernetLinkConfig.1.php"; else if ($SERVICE == "WANIPConn1") $php = "NOTIFY.WANIPConnection.1.php";
else if ($SERVICE == "WFAWLANConfig1") $php = "NOTIFY.WFAWLANConfig.1.php";
if ($METHOD == "SUBSCRIBE") { if ($SID == "") GENA_subscribe_new($gena_path, $HOST, $REMOTE, $URI, $TIMEOUT, $SHELL_FILE, "/htdocs/upnp/".$php, $INF_UID); else GENA_subscribe_sid($gena_path, $SID, $TIMEOUT); } else if ($METHOD == "UNSUBSCRIBE") { GENA_unsubscribe($gena_path, $SID); } ?>
|
可以看到当方法为SUBSCRIBE
时,会执行GENA_subscribe_new
函数
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
| function GENA_subscribe_new($node_base, $host, $remote, $uri, $timeout, $shell_file, $target_php, $inf_uid) { anchor($node_base); $count = query("subscription#"); $found = 0; foreach ("subscription") { if (query("host")==$host && query("uri")==$uri) {$found = $InDeX; break;} } if ($found == 0) { $index = $count + 1; $new_uuid = "uuid:".query("/runtime/genuuid"); } else { $index = $found; $new_uuid = query("subscription:".$index."/uuid"); }
if ($timeout==0 || $timeout=="") {$timeout = 0; $new_timeout = 0;} else {$new_timeout = query("/runtime/device/uptime") + $timeout;} set("subscription:".$index."/remote", $remote); set("subscription:".$index."/uuid", $new_uuid); set("subscription:".$index."/host", $host); set("subscription:".$index."/uri", $uri); set("subscription:".$index."/timeout", $new_timeout); set("subscription:".$index."/seq", "1");
GENA_subscribe_http_resp($new_uuid, $timeout); GENA_notify_init($shell_file, $target_php, $inf_uid, $host, $uri, $new_uuid); }
|
我们只看shell_file
参数被谁调用了,继续跟进GENA_notify_init
函数
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
| function GENA_notify_init($shell_file, $target_php, $inf_uid, $host, $uri, $sid) {
$inf_path = XNODE_getpathbytarget("", "inf", "uid", $inf_uid, 0); if ($inf_path=="") { TRACE_debug("can't find inf_path by $inf_uid=".$inf_uid."!"); return ""; } $phyinf = PHYINF_getifname(query($inf_path."/phyinf")); if ($phyinf == "") { TRACE_debug("can't get phyinf by $inf_uid=".$inf_uid."!"); return ""; }
$upnpmsg = query("/runtime/upnpmsg"); if ($upnpmsg == "") $upnpmsg = "/dev/null"; fwrite(w, $shell_file, "#!/bin/sh\n". 'echo "[$0] ..." > '.$upnpmsg."\n". "xmldbc -P ".$target_php. " -V INF_UID=".$inf_uid. " -V HDR_URL=".SECURITY_prevent_shell_inject($uri). " -V HDR_HOST=".SECURITY_prevent_shell_inject($host). " -V HDR_SID=".SECURITY_prevent_shell_inject($sid). " -V HDR_SEQ=0". " | httpc -i ".$phyinf." -d ".SECURITY_prevent_shell_inject($host)." -p TCP > ".$upnpmsg."\n" ); fwrite(a, $shell_file, "rm -f ".$shell_file."\n"); }
|
这里调用了两次fwrite
函数,第一次创建了.sh文件,并告诉系统是个shell文件,并且在IDA中看到确实是.sh形式传入的,第二次fwrite写入"rm -f ".$shell_file."\n"
命令,这里也就是我们的命令拼接的地方;
因为shell命令会将''
包裹的内容作为变量,会先执行里面的内容;
所以我们将/gena.cgi?service=shell_file
中的shell_file
控制位单引号包裹的命令即可;
总结
我们说的**UPnP(Universal Plug and Play)**即通用即插即用协议,其作用gena.cgi简单来说就是可以当我们支持UNPN协议的设备开启该协议,当主机或主机上的应用程序向该设备发出端口映射请求时,我们的设备就会自动为主机分配端口并进行端口映射。