A ZTE Wi-Fi router similar in family to the ZXHN H168N affected by CVE-2021-21735

CVE-2021-21735: From Unauthenticated Information Leak to Full Admin Compromise on ZTE ZXHN H168N

Source & attribution. This is an original English rewrite of the writeup “Unauthenticated Information Leak to Full Admin Compromise on ZTE ZXHN H168N (CVE-2021-21735)” by Mina Zekry, published at minanagehsalalma.github.io. The accompanying public PoC repository is at github.com/minanagehsalalma/cve-2021-21735-zte-zxhn-h168n-admin-compromise. All technical observations, evidence images, code excerpts and the disclosure timeline are credited to the original author; this post rephrases the prose, reproduces the original code & evidence assets verbatim, and adds editorial framing for engineers reading on core-jmp.org. Please read the original for the canonical narrative.

Executive Summary

CVE-2021-21735 is, on paper, an information-disclosure bug in the ZTE ZXHN H168N residential gateway. In practice it is a privileged-data leak that hands an unauthenticated attacker the very identifiers that some ISP deployments use as the operational administrator secret — collapsing the boundary between “low-severity” data exposure and full administrative compromise of the device. Two wizard handlers, wizard_pppoe_lua.lua and wizard_wlan_config_lua.lua, returned structured XML responses containing PPPoE identifiers, SSID configuration and the plaintext Wi-Fi passphrase — all reachable through a filename whitelist that was never meant to govern privileged data routes.

The author of the original writeup, Mina Zekry, walks through the exposure, the route-level authorization failure that made it possible, the broader exploitation chain (including a browser-delivered variant via rogue access points), and the noticeable disagreement between ZTE’s self-rating of 3.5 Low and NVD’s 6.5 Medium. The split itself is the most useful artifact for defenders: it is a textbook example of how vendor-side scoring can understate a bug whose downstream impact depends on operational context, not on the immediate CIA triad of the call itself.

At a Glance

FieldValue
Affected firmwareV3.5.0_EG1T4_TE and earlier
Resolved inV3.5.0_EG1T10_ETS
Leaked dataPPPoE identifiers, SSID data, Wi-Fi passphrase
Operational impactAdmin takeover and WLAN compromise
PoC scriptzte_zxhn_h168n_bulk_poc.py
ZTE rating3.5 Low
NVD rating6.5 Medium
Key route/wizard_page/
Disclosure openedFebruary 12, 2021
Summary of CVE-2021-21735. Source: original article.

What Makes This One Serious

A great many router CVEs end their public lives as weakly-worded “information disclosure” tickets and are then quietly forgotten. This one is different because the leaked data is not passive telemetry — it is identity and access material with downstream meaning in real ISP environments. The wizard handlers expose the values that, when combined, give an attacker administrative footing and an easy lateral path into the Wi-Fi network.

The original analysis frames the failure as three stacked problems:

  • Exposure. PPPoE and WLAN endpoints answer requests before a meaningful authorization boundary is enforced.
  • Escalation. The leaked PPPoE identifiers can be reused, in many ISP deployments, as the effective administrator secret — converting the disclosure into a takeover.
  • Breakout. A leaked Wi-Fi passphrase collapses WPA2 strength regardless of how long or complex it is; the attacker simply walks in.

Exploit Path

The disclosure centres on two wizard handlers, wizard_pppoe_lua.lua and wizard_wlan_config_lua.lua. They behave like privileged backend endpoints that have been routed through a weak setup surface. From an unauthenticated network position the attacker can issue plain HTTP requests to /wizard_page/ and pull back structured XML containing values that should sit behind an authenticated configuration boundary.

The skeleton of the interaction looks like this — reproduced verbatim from the original writeup:

GET  /wizard_page/wizard_pppoe_lua.lua
GET  /wizard_page/wizard_wlan_config_lua.lua
POST /wizard_page/wizard_wlan_config_lua.lua

IF_ACTION=GetPassword
&_InstID_PASS=DEV.WIFI.AP1.PSK1
&PASSTYPE=PSK

POST /wizard_page/wizard_wlan_config_lua.lua HTTP/1.1
Host: router-ip
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest

IF_ACTION=GetPassword&_InstID_PASS=DEV.WIFI.AP1.PSK1&PASSTYPE=PSK

HTTP/1.1 200 OK
Content-Type: application/xml

<ajax_response>
  <IF_ERRORSTR>SUCC</IF_ERRORSTR>
  <ParaName>KeyPassphrase</ParaName>
  <ParaValue>[REDACTED-WIFI-PASSWORD]</ParaValue>
</ajax_response>

The same pattern works against the PPPoE handler: an unauthenticated request returns the subscriber’s PPPoE identifier in a structured XML response. The author packages this into a bulk PoC script (zte_zxhn_h168n_bulk_poc.py) for sweeping multiple devices, and also describes a browser-delivered variant that abuses rogue access points to trigger the same calls from a victim’s own browser session. The substrate — secret-bearing routes reachable through a setup whitelist that should never have governed them — is stable across both deliveries.

Root Cause Analysis

The firmware contains a helper that decides whether a given page name is allowed to be served before authentication. The structure is essentially the snippet below — the QuickSetup feature flag effectively whitelists a small set of filenames, and those filenames include the privileged wizard handlers:

WizardTable = cmapi.getinst("OBJ_WEB_QUICKSETUP_ID", "")
WizardFlag  = WizardTable.QuickSetupEnable

if WizardFlag == "0" then
    whitelist["wizardFrame_finishAction.lua"] = 1
    whitelist["wizard_pppoe_lua.lua"]         = 1
    whitelist["wizard_wlan_config_lua.lua"]   = 1
end

This pro_urlsafe_IsWhiteList(pageName)-style helper is doing several jobs at once, and that conflation is where the bug lives:

1. Filename routing was used as an authorization primitive.

The check is “is this filename in the whitelist?” — not “does this caller hold the privileges this route requires?” A whitelist of strings is a syntactic gate; authorization is a semantic property. Treating one as the other is a category error that recurs across embedded web stacks.

2. Setup UI and privileged data handlers were treated as the same class of object.

The wizard endpoints are not really UI pages — they expose backend operations such as “return the current Wi-Fi key”. Lumping them in with cosmetic setup routes meant that a single “allow during setup” bit governed both classes.

3. Global setup state replaced per-request privilege checks.

Whether the QuickSetup flow is “enabled” is global device state. Using that single global to gate sensitive per-request operations means there is no opportunity to re-evaluate authorization in the request context where it actually matters.

4. The endpoint appears not to enforce an internal second gate.

Defence in depth would have the handlers re-check whether the caller has the right to read PSK material, regardless of whether the route was allowed through the front door. The observed behaviour suggests no such second gate is present.

5. The exploit chain becomes critical because the leaked data is security-significant.

If the leaked values were anonymous diagnostic counters, the bug would be a footnote. Because the values include the Wi-Fi passphrase and the PPPoE identifier — the latter doubling as the admin secret in some ISP fleets — the “information disclosure” label dramatically understates the operational blast radius.

Visual Proof

The author publishes a small but pointed evidence set: a screenshot of the firmware whitelist code that ties the routes to the setup-allowed path, and a redacted bulk extraction of URLs, PPPoE identifiers, SSIDs and Wi-Fi passphrases captured from the field. Together they show both the design failure and its operational impact in one go.

Firmware logic showing QuickSetup whitelist entries for wizard_pppoe_lua.lua and wizard_wlan_config_lua.lua routes
Firmware logic showing QuickSetup whitelist entries. The helper routes wizard_pppoe_lua.lua and wizard_wlan_config_lua.lua into a setup-allowed path. Source: original article.
Screenshot showing extracted router URLs, PPPoE identifiers, SSIDs, and Wi-Fi passphrases in bulk (redacted)
Historical field proof captured in a local directory: URLs, PPPoE identifiers, SSIDs and Wi-Fi passphrases displayed in bulk. Source: original article.

Disclosure Timeline

DateEvent
2021-02-12Initial disclosure to ZTE PSIRT.
2021-02-15Follow-up noting that the affected functionality also appears capable of modifying settings.
2021-03-01ZTE confirms the vulnerability and offers a bounty.
2021-03-09ZTE communicates a 3.5 Low rating with the corresponding CVSS vector.
2021-03-30ZTE states it will not disclose the CVE until the issue is fixed.
2021-06-09ZTE publishes its security bulletin and lists the resolved firmware version.
2021-09-02ZTE informs the reporter that CVE-2021-21735 has been assigned.
2021-09-28ZTE confirms that the bounty payout has been completed.
Coordinated disclosure timeline as published in the original writeup. Source: original article.

Severity Split: ZTE vs. NVD

The most instructive non-technical artifact in this report is the gap between the two published severity scores. The disagreement is not a paperwork detail; it reflects two different mental models of the bug.

ZTE View — 3.5 Low

ZTE applies an adjacent-network attack vector with low confidentiality impact (AV:A/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N). The scoring describes a narrow exposure scenario in which only some structured XML is returned to a caller who is already “on the LAN” with low-level privileges — an attacker model that intentionally excludes the operational consequence of what is being leaked.

NVD View — 6.5 Medium

NVD treats the same bug with a network-vector profile and high confidentiality impact. That better matches a real-world case where the leaked PPPoE/WLAN data can materially unlock administrator access and Wi-Fi access in the deployments where those values double as credentials. The original author argues that the latter framing is the right one: the “information disclosure” label understates the operational blast radius, and a defender should plan for the NVD interpretation, not the vendor’s.

Key Takeaways

  • A filename whitelist is not an authorization decision — if you have to whitelist by string, you have already lost the structural argument that the route is safe.
  • Setup UIs and privileged data endpoints belong to different security classes; collapsing them under one feature flag is a recurring root cause in embedded web stacks.
  • The severity of an information-disclosure bug is a function of what the leaked values mean downstream — PPPoE identifiers that double as admin secrets are not “low-impact” in any honest threat model.
  • WPA2 passphrase length and complexity provide zero protection against an unauthenticated XML endpoint that returns the passphrase in cleartext.
  • Vendor self-scoring frequently understates impact when the bug’s consequence depends on deployment context; pull the NVD vector and re-derive your risk from it.
  • Browser-delivered variants of router bugs (rogue AP or CSRF-style chains) extend the attack surface beyond “same LAN” threat models and should be assumed for any unauthenticated router endpoint.
  • Coordinated disclosure on consumer-router CVEs remains slow: from initial report (Feb 2021) to CVE assignment (Sep 2021) was over seven months even with vendor engagement.

Defensive Recommendations

  • Upgrade firmware. Move affected ZTE ZXHN H168N devices to V3.5.0_EG1T10_ETS or later. If you operate a fleet, audit by firmware version — not by purchase date.
  • Treat PPPoE identifiers as secrets where deployments use them as such. If a leaked PPPoE username/password also functions as the device administrator credential in your ISP fleet, classify them at the same sensitivity as any other admin secret and rotate accordingly after exposure.
  • Rotate Wi-Fi passphrases on any device that has ever been Internet-reachable on a vulnerable firmware. WPA2 strength is moot once the PSK has been disclosed; assume it has been and rotate.
  • Replace filename whitelists with proper request-time authorization. If you ship embedded web code, audit anything that resembles whitelist[pageName] = 1 and convert it into per-handler privilege checks evaluated on the actual request context.
  • Add a second internal gate. Even where a route is allowed through the front door, the handler should independently re-check that the caller may read the data it returns (especially for any handler that touches credential material).
  • Block /wizard_page/ from the WAN side. Setup UIs should never be reachable from the Internet. Verify with an external scan rather than trusting documentation.
  • Monitor for browser-delivered variants. Look for unexpected outbound XHRs from client devices toward router IPs (RFC1918 or carrier-private space) that target /wizard_page/ — these are signatures of CSRF/rogue-AP chains.
  • Track NVD scoring, not vendor scoring, in your risk register. Where the two disagree, prefer the higher score until you can independently justify a lower one in your environment.

Conclusion

CVE-2021-21735 is a clean illustration of how a small structural shortcut — using a filename whitelist to govern privileged backend routes — turns into a takeover when the values behind those routes are credentials in disguise. The most transferable lesson here is not the specific endpoint or the specific firmware version, but the framing: information disclosure is only as “informational” as the values it discloses. Mina Zekry’s writeup makes that point convincingly and concretely, and the ZTE vs. NVD severity split is a useful artifact in itself for anyone arguing the same point inside their own organisation. For canonical detail, the original article and the public PoC repository remain the authoritative references; the rest of this post is editorial framing on top of that work.

References

Credit and thanks to the original author, Mina Zekry, whose research, evidence and disclosure work this post is built on. Read the canonical writeup at minanagehsalalma.github.io/cve-2021-21735-zte-zxhn-h168n-admin-compromise.

Comments are closed.