D-Link_DIR-859RCE漏洞CVE-2019-17621复现

D-Link_DIR-859RCE漏洞CVE-2019-17621复现

Berial Pwn

漏洞描述

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

# 创建名为 tap0 的虚拟网络接口
sudo tunctl -t tap0

# 配置 tap0 接口的 IP 地址和子网掩码
sudo ip addr add 192.168.0.4/24 dev tap0
sudo ip link set tap0 up

# 启用 IP 转发
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null

# 配置 NAT (假设物理机外网接口是 ens33)
sudo iptables -t nat -A POSTROUTING -o ens33 -j MASQUERADE

# 允许 tap0 接口的数据包转发
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
image-20250330162733537

分析下httpd

image-20250330163011409
image-20250330163011409

发现确实打开这个了,但是系统中没有这个。

漏洞分析

根据漏洞描述逆向分析cgibin

image-20250330164130876
image-20250330164130876

image-20250330165635751
image-20250330165635751

跟进xmldbc_ephp函数,套了好几层,主要是发送了前面的报文

image-20250330165815319
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);

/* IGD services */
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";
/* WFA services */
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;
/* find subscription index & uuid */
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");
}

/* get timeout */
if ($timeout==0 || $timeout=="") {$timeout = 0; $new_timeout = 0;}
else {$new_timeout = query("/runtime/device/uptime") + $timeout;}
/* set to nodes */
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协议的设备开启该协议,当主机或主机上的应用程序向该设备发出端口映射请求时,我们的设备就会自动为主机分配端口并进行端口映射。

  • 标题: D-Link_DIR-859RCE漏洞CVE-2019-17621复现
  • 作者: Berial
  • 创建于 : 2025-03-30 16:22:09
  • 更新于 : 2025-03-30 17:14:42
  • 链接: https://berial.cn/posts/D-Link_DIR-859RCE漏洞CVE-2019-17621复现.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
D-Link_DIR-859RCE漏洞CVE-2019-17621复现