国内某知名路由器固件存在隐藏后门,可用任意用户名登管理员账户"

admin 2026-10-23 11:40:12 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

国内某知名路由器固件存在隐藏后门,可用任意用户名登管理员账户

原创

suntiger
suntiger

二进制空间安全

2026年7月20日 15:25
北京

在小说阅读器读本章

去阅读

将二进制空间安全设为\"星标⭐️\"

第一时间收到文章更新

事件背景

根据公开报道(https://kb.cert.org/vuls/id/213560),该路由器厂商至少有5个版本固件在Web管理服务器/bin/httpd的login()函数中,嵌入了未向用户披露的第二条认证通道。攻击者如果掌握配置项sys.rzadmin.password中的隐藏密码,可在 username填写任务字符串的情况下获得role=2的管理员权限,并创建合法session,该步骤与用户自己设置的管理员口令没有任何联系。

对于普通企业和家庭用户而言,该后门在管理界面中完全不可见、关不掉,甚至修改管理员登录密码也无法消除风险,在开启远程Web管理时,攻击暴露面将从局域网扩散到互联网,高风险大幅度增加。

login()双路径认证架构

根据公开信息, 该后门位于Web服务器二进制/bin/httpd的login()函数,当用户访问路由器管理页面,例如:192.168.0.1, 提交用户名和密码后,login()函数理论上会走合规正常流程,例如:

用户 password    │    ▼PasswordToMd5()          ← MD5 哈希    │    ▼check_rand_key()         ← 随机数/会话密钥校验    │    ▼prod_encode64()          ← 与存储值比对    │    ▼成功 → 创建 Session → 分配 role

但如果上述正常验证流程失败,函数并不总是返回密码错误,而是进入隐藏分支:

if (normal_auth_failed) {    char backdoor_pwd[256];    GetValue(\"sys.rzadmin.password\", backdoor_pwd);
    if (strcmp(backdoor_pwd, user_supplied_password) == 0) {        /* username 在此分支不参与校验 */        session.role = 2;   /* 管理员 */        create_valid_session();        return SUCCESS;    }}return AUTH_FAILED;

当攻击者拿到role=2的管理员账号权限后,可以更改路由器的DNS、防火墙、端口转发、无线密钥等, 为进一步进行横向渗透提供了有利条件。

受影响固件与暴露面评估

目前已经确认的固件版本包括:

型号系列 固件版本字符串
FH1201 US\_FH1201V1.0BR\_V1.2.0.14(408)\_EN\_TD
W15E US\_W15EV1.0br\_V15.11.0.5(1068\_1567\_841)\_EN\_TDE
AC10 US\_AC10V1.0re\_V15.03.06.46\_multi\_TDE01
AC5 US\_AC5V1.0RTL\_V15.03.06.48\_multi\_TDE01
AC6 V2 US\_AC6V2.0RTL\_V15.03.06.51\_multi\_T

需要注意的是,上面未被列出的固件并不能视为安全,同厂商/bin/httpd代码树可能在其它区域版本复用。

根据该漏洞形成原理,概念型攻击链流程如下:

(1).通过固件逆向、配置提取、内部泄露等方式,获取到目标版本固件的隐藏密码。

(2).访问https:///管理口路径。

(3).POST login:username=任意,password=<隐藏密码>。

(4).获取role=2管理员权限Session,登录后获取Web管理权限。

企业分层防御与缓解措施

在无官方补丁的前提下,企业策略必须区分缓解和根治,如果要根治,目前只能依赖换硬件或等待厂商固件更新。官方推荐的缓解措施主要包括:关闭远程Web管理和修改默认LAN IP。目的是阻断WAN侧利用以及降低对192.168.0.1的扫描命中。

在网络层之外,安全团队可在现有的SIEM/NDR能力之上叠加以下低误报导向的检测线索,主要包括:

(1).同一源IP对管理口多次失败后单次成功。

(2).非变更窗口的DNS服务器修改、静态路由、DMZ、端口转发新增。

(3).无线SSID密码或WPS状态异常变更。

(4).管理Session创建时间与已知变更工单不匹配。

企业对自身资产隐患进行排查阶段性工作可按以下方案进行:

阶段一:资产盘点

该阶段目标是把\"可能受影响\"变成可枚举资产列表,动作包括:从CMDB、DHCP指纹、网关ARP表收集Tenda设备,登录管理页收集完整固件版本字符串,标记是否曾开启远程Web管理、是否使用默认192.168.0.1。最终收集完成三项物料清单,分别为:受影响/待确认/不受影响,以及责任人。

阶段二:暴露面收敛

对已命中或待确认设备:立即关闭远程管理;将管理口迁入独立 VLAN,ACL 仅允许 jump host / 运维网段;修改LAN网关地址降低扫描噪音;对解包得到的 /bin/httpd 运行strings特征扫描。

阶段三:根治与持续运营

根治路径只有更换已确认不受影响设备或应用厂商修复固件,持续运营建议:新采购路由器纳入固件strings抽检;禁止Internet-facing远程Web管理写入基线;关注官方补丁,

务必对 /bin/httpd 做 bindiff,确认 login() 中 sys.rzadmin.password 分支已移除,而非仅改默认密码值。

固件与资产自动化核查

安全团队从设备或官网下载固件,解包得到/bin/httpd,需批量筛查是否包含rzadmin配置键,运行实例如下:

cd tendaBackdoorArticle/srcpython tenda_backdoor_assessor.py --scan-firmware ./firmware_samples/httpd

预期扫描出结果如下:

=== Tenda CVE-2026-11405 Assessment: FAIL ===Target: firmware_samples/httpd
[1] [CRITICAL] backdoor_indicator: 检测到后门配置键 sys.rzadmin.password(httpd)    → 标记为 CVE-2026-11405 疑似受影响固件,禁止上线并计划换机

在内网盘点时确认网关管理口是否可从运维网段意外暴露,并尝试从 HTTP 首页提取固件版本线索。

python tenda_backdoor_assessor.py --probe 192.168.0.1:80

预期输出结果如下:

[1] [MEDIUM] exposure: 管理口 TCP 80 可达    → 确认是否应从WAN暴露;建议关闭远程Web管理

检测脚本代码如下:

#!/usr/bin/env python3# -*- coding: utf-8 -*-\"\"\"Tenda CVE-2026-11405 固件与资产自动化核查工具(防御向)功能:  1. 固件二进制 strings 扫描 — 检测 sys.rzadmin.password 等后门特征  2. 受影响固件版本字符串匹配  3. 路由器管理口暴露面探测(HTTP 可达性,不尝试后门密码)  4. 综合 JSON 报告 + 控制台 PASS/FAIL用法:  python tenda_backdoor_assessor.py --config routers.yaml  python tenda_backdoor_assessor.py --scan-firmware ./firmware_samples/httpd  python tenda_backdoor_assessor.py --probe 192.168.0.1:80\"\"\"from __future__ import annotationsimport argparseimport jsonimport reimport socketimport sysfrom dataclasses import asdict, dataclass, fieldfrom pathlib import Pathfrom typing import Anyfrom urllib.error import URLErrorfrom urllib.request import Request, urlopentry:    import yamlexcept ImportError:    yaml = None  # type: ignoreDEFAULT_INDICATORS = (    \"sys.rzadmin.password\",    \"rzadmin\",    \"GetValue\",    \"PasswordToMd5\",    \"check_rand_key\",    \"prod_encode64\",)DEFAULT_AFFECTED_FIRMWARE = [    \"US_FH1201V1.0BR_V1.2.0.14(408)_EN_TD\",    \"US_W15EV1.0br_V15.11.0.5(1068_1567_841)_EN_TDE\",    \"US_AC10V1.0re_V15.03.06.46_multi_TDE01\",    \"US_AC5V1.0RTL_V15.03.06.48_multi_TDE01\",    \"US_AC6V2.0RTL_V15.03.06.51_multi_T\",]FIRMWARE_VERSION_RE = re.compile(    r\"US_(?:FH1201|W15E|AC10|AC5|AC6)[^\\s\\\"']{8,80}\",    re.IGNORECASE,)@dataclassclass Finding:    category: str    severity: str  # CRITICAL | HIGH | MEDIUM | LOW    message: str    recommendation: str = \"\"    context: dict[str, Any] = field(default_factory=dict)@dataclassclass AssessmentReport:    target: str    findings: list[Finding] = field(default_factory=list)    passed: bool = True    def add(self, finding: Finding) -> None:        self.findings.append(finding)        if finding.severity in (\"CRITICAL\", \"HIGH\", \"MEDIUM\"):            self.passed = Falsedef load_config(path: Path) -> dict[str, Any]:    if yaml is None:        raise SystemExit(\"需要 PyYAML: pip install pyyaml\")    with path.open(encoding=\"utf-8\") as f:        return yaml.safe_load(f) or {}def extract_strings(data: bytes, min_len: int = 6) -> list[str]:    \"\"\"从二进制中提取可打印 ASCII 字符串。\"\"\"    pattern = re.compile(rb\"[\\x20-\\x7e]{%d,}\" % min_len)    return [m.group().decode(\"ascii\", errors=\"ignore\") for m in pattern.finditer(data)]def scan_firmware_file(path: Path, indicators: tuple[str, ...]) -> AssessmentReport:    report = AssessmentReport(target=str(path))    try:        data = path.read_bytes()    except OSError as exc:        report.add(            Finding(                category=\"firmware_io\",                severity=\"HIGH\",                message=f\"无法读取固件文件: {exc}\",                recommendation=\"确认路径与文件权限\",            )        )        return report    strings = extract_strings(data)    joined = \"\
\".join(strings)    hits = [s for s in indicators if s in joined]    if \"sys.rzadmin.password\" in hits:        report.add(            Finding(                category=\"backdoor_indicator\",                severity=\"CRITICAL\",                message=f\"检测到后门配置键 sys.rzadmin.password({path.name})\",                recommendation=\"标记为 CVE-2026-11405 疑似受影响固件,禁止上线并计划换机\",                context={\"matched_indicators\": hits},            )        )    elif len(hits) >= 3:        report.add(            Finding(                category=\"backdoor_indicator\",                severity=\"HIGH\",                message=f\"检测到 {len(hits)} 个 login() 相关特征字符串\",                recommendation=\"对 /bin/httpd 做 bindiff 或逆向确认是否存在隐藏认证分支\",                context={\"matched_indicators\": hits},            )        )    for ver in FIRMWARE_VERSION_RE.findall(joined):        report.add(            Finding(                category=\"firmware_version\",                severity=\"MEDIUM\",                message=f\"固件内嵌版本字符串: {ver}\",                recommendation=\"与 CERT/CC 受影响列表交叉核对\",                context={\"version_string\": ver},            )        )    if not report.findings:        report.add(            Finding(                category=\"backdoor_indicator\",                severity=\"LOW\",                message=\"未命中已知后门特征(不代表安全,可能为不同 SDK 分支)\",                recommendation=\"结合设备型号与供应链信息继续排查\",            )        )    return reportdef scan_firmware_dirs(dirs: list[str], indicators: tuple[str, ...]) -> AssessmentReport:    report = AssessmentReport(target=\"firmware_batch\")    paths: list[Path] = []    for d in dirs:        base = Path(d)        if not base.exists():            report.add(                Finding(                    category=\"firmware_io\",                    severity=\"MEDIUM\",                    message=f\"固件目录不存在: {base}\",                    recommendation=\"创建目录并放入待测 httpd 或完整固件镜像\",                )            )            continue        paths.extend(p for p in base.rglob(\"*\") if p.is_file())    if not paths:        report.add(            Finding(                category=\"firmware_io\",                severity=\"MEDIUM\",                message=\"固件目录为空,跳过 strings 扫描\",                recommendation=\"将 /bin/httpd 或 squashfs 解包文件放入 firmware_scan_dirs\",            )        )        return report    for path in paths:        sub = scan_firmware_file(path, indicators)        report.findings.extend(sub.findings)        if not sub.passed:            report.passed = False    return reportdef match_affected_firmware(version: str, affected: list[str]) -> bool:    version_norm = version.strip()    return any(version_norm == item or item in version_norm for item in affected)def probe_mgmt_port(host: str, port: int, timeout: float = 3.0) -> dict[str, Any]:    \"\"\"探测管理口 TCP 可达性与 HTTP 响应头(不提交登录表单)。\"\"\"    result: dict[str, Any] = {\"host\": host, \"port\": port, \"tcp_open\": False, \"http_status\": None}    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    sock.settimeout(timeout)    try:        sock.connect((host, port))        result[\"tcp_open\"] = True    except OSError:        return result    finally:        sock.close()    scheme = \"https\" if port == 443 else \"http\"    url = f\"{scheme}://{host}:{port}/\"    try:        req = Request(url, headers={\"User-Agent\": \"TendaAssessor/1.0 (defensive inventory)\"})        with urlopen(req, timeout=timeout) as resp:            result[\"http_status\"] = resp.status            body = resp.read(8192).decode(\"utf-8\", errors=\"ignore\")            if \"tenda\" in body.lower():                result[\"vendor_hint\"] = \"Tenda-like UI detected\"            fw = re.search(r\"(?:firmware|version)[^<]{0,40}([0-9A-Za-z_().]+)\", body, re.I)            if fw:                result[\"firmware_guess\"] = fw.group(1)[:80]    except URLError as exc:        result[\"http_error\"] = str(exc.reason) if hasattr(exc, \"reason\") else str(exc)    return resultdef assess_router_target(    host: str,    port: int,    affected: list[str],    expect_remote: bool,) -> AssessmentReport:    report = AssessmentReport(target=f\"{host}:{port}\")    probe = probe_mgmt_port(host, port)    if probe.get(\"tcp_open\"):        report.add(            Finding(                category=\"exposure\",                severity=\"MEDIUM\" if not expect_remote else \"HIGH\",                message=f\"管理口 TCP {port} 可达\",                recommendation=\"确认是否应从 WAN 暴露;CERT/CC 建议关闭远程 Web 管理\",                context=probe,            )        )    else:        report.add(            Finding(                category=\"exposure\",                severity=\"LOW\",                message=f\"管理口 {host}:{port} 不可达(可能离线或非 Tenda)\",                context=probe,            )        )        return report    fw_guess = probe.get(\"firmware_guess\", \"\")    if fw_guess and match_affected_firmware(fw_guess, affected):        report.add(            Finding(                category=\"affected_firmware\",                severity=\"CRITICAL\",                message=f\"管理页疑似匹配受影响固件: {fw_guess}\",                recommendation=\"立即关闭远程管理、隔离 VLAN,计划换机或等待官方补丁\",                context={\"firmware_guess\": fw_guess},            )        )    if expect_remote:        report.add(            Finding(                category=\"exposure\",                severity=\"CRITICAL\",                message=\"配置标记 expect_remote_mgmt=true — WAN 侧管理口暴露\",                recommendation=\"关闭远程 Web 管理并修改默认 LAN IP(CERT/CC 官方缓解)\",            )        )    return reportdef run_full_assessment(config_path: Path) -> AssessmentReport:    cfg = load_config(config_path)    affected = list(cfg.get(\"affected_firmware\", []))    indicators = tuple(cfg.get(\"backdoor_indicators\", DEFAULT_INDICATORS))    report = AssessmentReport(target=str(config_path))    fw_report = scan_firmware_dirs(cfg.get(\"firmware_scan_dirs\", []), indicators)    report.findings.extend(fw_report.findings)    for item in cfg.get(\"router_targets\", []):        host = item.get(\"host\", \"\")        port = int(item.get(\"mgmt_port\", 80))        expect = bool(item.get(\"expect_remote_mgmt\", False))        if not host:            continue        sub = assess_router_target(host, port, affected, expect)        report.findings.extend(sub.findings)        if not sub.passed:            report.passed = False    if fw_report.passed and all(        f.severity not in (\"CRITICAL\", \"HIGH\", \"MEDIUM\") for f in report.findings    ):        report.passed = True    return reportdef print_report(report: AssessmentReport) -> None:    status = \"PASS\" if report.passed else \"FAIL\"    print(f\"\
=== Tenda CVE-2026-11405 Assessment: {status} ===\")    print(f\"Target: {report.target}\
\")    for i, f in enumerate(report.findings, 1):        print(f\"[{i}] [{f.severity}] {f.category}: {f.message}\")        if f.recommendation:            print(f\"    → {f.recommendation}\")    print()def main() -> int:    parser = argparse.ArgumentParser(description=\"Tenda CVE-2026-11405 防御向核查工具\")    parser.add_argument(\"--config\", type=Path, help=\"YAML 配置文件路径\")    parser.add_argument(\"--scan-firmware\", type=Path, help=\"单个固件/httpd 文件 strings 扫描\")    parser.add_argument(\"--probe\", help=\"探测管理口 host:port,例 192.168.0.1:80\")    parser.add_argument(\"--json-out\", type=Path, help=\"输出 JSON 报告路径\")    args = parser.parse_args()    if args.scan_firmware:        report = scan_firmware_file(args.scan_firmware, DEFAULT_INDICATORS)    elif args.probe:        host, _, port_str = args.probe.partition(\":\")        port = int(port_str or \"80\")        report = assess_router_target(host, port, DEFAULT_AFFECTED_FIRMWARE, False)    elif args.config:        report = run_full_assessment(args.config)    else:        parser.print_help()        return 1    print_report(report)    if args.json_out:        payload = {\"passed\": report.passed, \"target\": report.target, \"findings\": [asdict(f) for f in report.findings]}        args.json_out.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding=\"utf-8\")        print(f\"JSON report written to {args.json_out}\")    return 0 if report.passed else 2if __name__ == \"__main__\":    sys.exit(main())

(全文完)


国内某知名路由器固件存在隐藏后门,可用任意用户名登管理员账户" 网络安全文章

国内某知名路由器固件存在隐藏后门,可用任意用户名登管理员账户"

该文章披露了某知名路由器固件在Web管理服务器login()函数中存在隐藏后门,攻击者利用配置项sys.rzadmin.password中的隐藏密码,可在username字段填写任意字符串的情况下获取管理员权限。受影响固件包括多个型号版本,后门在管理界面不可见且无法通过修改密码消除。文章提供了关闭远程管理、修改默认IP等缓解措施,并给出了固件资产自动化核查的检测脚本。
安永数据泄露事件" 网络安全文章

安永数据泄露事件"

安永律师事务所(EY)在2026年春季遭遇数据泄露,未经授权第三方通过其IT支持工单平台在3月28日至4月12日期间下载了包含客户税务数据的文件,4月23日发现异常。泄露文件含个人信息和财务信息,目前无证据表明数据被滥用。该事件与安永此前其他安全漏洞无关,但凸显了第三方IT服务平台的安全风险。
当法国正考虑购买飞马间谍软件时,摩洛哥却在秘密监听其部长" 网络安全文章

当法国正考虑购买飞马间谍软件时,摩洛哥却在秘密监听其部长"

法国曾考虑以6000万至8000万欧元购买NSO集团的飞马间谍软件,但2020年底因声誉风险和国家主权被总统否决。同期摩洛哥利用该软件监听了七位法国部长的手机,包括前总理勒科尔尼。感染痕迹指向摩洛哥,法国情报机构确认其间谍活动损害法国利益。建议加强网络安全监管,警惕间谍软件滥用。
评论:0   参与:  0