漏洞报告 #3480039 - WebSocket逻辑错误:控制帧(PING/PONG)饥饿导致大数据传输期间连接中断(DoS)
提交者: efrsxcv
提交日期: 3天前
摘要
我在 lib/ws.c 中发现了一个关于WebSocket控制帧(PING/PONG)处理的逻辑缺陷。根据RFC 6455,控制帧应尽可能快地处理,即使是在处理分片数据帧的过程中,以维持连接状态(保活)。然而,当libcurl通过 curl_ws_send 主动发送大量数据流时,它未能优先处理PONG响应。如果 payload_remain > 0,PONG响应会被排在用户数据之后。如果传输时间超过服务器的保活超时时间,服务器将断开连接,导致合法的操作遭到拒绝服务攻击。
受影响版本: 在最新的curl master分支(以及支持WebSocket的近期版本)上复现。(请在终端中运行 curl -V 并粘贴输出)
复现步骤
为了复现此问题,我们需要一个强制执行较短保活超时的“严格”WebSocket服务器,以及一个使发送队列饱和的客户端。
1. 设置恶意服务器(strict_ws_server.py)
此Python脚本模拟一个服务器,每1秒发送一个PING,如果在3秒内未收到PONG则断开连接。
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
import socket
import struct
import time
import threading
import select
OP_PING = 0x9
OP_PONG = 0xA
def create_frame(opcode, payload=b""):
b1 = 0x80 | opcode
b2 = len(payload) & 0x7F
return struct.pack("!BB", b1, b2) + payload
def handle_client(conn):
print("[+] Client connected. Handshaking...")
try:
req = conn.recv(4096)
resp = (
b"HTTP/1.1 101 Switching Protocols\r\n"
b"Upgrade: websocket\r\n"
b"Connection: Upgrade\r\n"
b"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
b"\r\n"
)
conn.sendall(resp)
print("[+] Handshake OK. Monitoring Heartbeat...")
last_pong_time = time.time()
running = True
def pinger():
nonlocal last_pong_time, running
while running:
try:
time.sleep(1)
conn.sendall(create_frame(OP_PING, b"alive?"))
if time.time() - last_pong_time > 3:
print("\n[!!!] TIMEOUT: Client did not reply PONG in 3s!")
running = False
conn.close()
return
except:
running = False
return
t = threading.Thread(target=pinger)
t.start()
while running:
ready = select.select([conn], [], [], 1)
if ready[0]:
data = conn.recv(1024)
if not data: break
opcode = data[0] & 0x0F
if opcode == OP_PONG:
last_pong_time = time.time()
except:
pass
def start_server():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0', 9090))
s.listen(1)
print("[*] Strict Server on 9090...")
while True:
conn, addr = s.accept()
handle_client(conn)
if __name__ == "__main__":
start_server()
|
2. 设置客户端PoC(poc_ws_starve.c)
使用以下命令编译:gcc poc_ws_starve.c -o poc_ws_starve -lcurl
此客户端在发送大量数据的同时,人为地稍微减慢速度以模拟网络延迟/繁忙循环,迫使PONG被排队。
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
|
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <curl/curl.h>
int main(void) {
CURL *curl;
CURLcode res;
curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
if(curl) {
// Connect to local strict server
curl_easy_setopt(curl, CURLOPT_URL, "ws://127.0.0.1:9090/");
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L);
res = curl_easy_perform(curl);
if(res != CURLE_OK) return 1;
size_t sent;
char buffer[1024];
memset(buffer, 'A', sizeof(buffer));
const struct curl_ws_frame *meta;
size_t rlen;
char rbuf[256];
// Send massive data stream
for(int i=0; i<100000; i++) {
// 1. Send Data (CURLWS_BINARY)
res = curl_ws_send(curl, buffer, sizeof(buffer), &sent, 0, CURLWS_BINARY);
if(res != CURLE_OK) break;
// 2. Sleep briefly to allow Server to send PING
usleep(10000);
// 3. Call recv to process incoming PING
// EXPECTED: Curl should reply PONG immediately.
// ACTUAL: Curl queues PONG behind the data stream because payload_remain > 0.
curl_ws_recv(curl, rbuf, sizeof(rbuf), &rlen, &meta);
}
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return 0;
}
|
3. 执行
- 在终端1运行
python3 strict_ws_server.py。
- 在终端2运行
./poc_ws_starve。
支持材料/参考资料
- RFC 6455 第5.5节:“控制帧(参见第5.5节)可以注入到分片消息的中间。”
- 根本原因: 在
lib/ws.c 的 ws_enc_add_cntrl 函数中,代码检查 if(!ws->enc.payload_remain)。如果正在发送数据,则返回 CURLE_OK,实际上导致PONG被排队而未发送,引起饥饿。
观察到的输出(服务器终端)
1
|
[!!!] TIMEOUT: Client did not reply PONG in 3s!
|
尽管客户端是活跃的,服务器还是断开了与客户端的连接,这证实了逻辑错误。
影响
摘要: 在有效操作中,由于控制帧饥饿导致连接意外断开,严格来说这影响了“可用性”。
时间线
-
3天前: efrsxcv 提交报告。
-
3天前: efrsxcv 附加了两张截图(Jepretan_layar_20251228_010812.png, Jepretan_layar_20251228_010808.png)。
-
3天前: bagder (curl 员工) 评论感谢报告,并表示将进行调查。
-
3天前: bagder 评论表示未看到安全角度。服务器同时发送内容和PING。这可能是错误,但可以像处理其他错误一样管理。
-
3天前: bagder 同意。
-
2天前: bagder 关闭报告并将状态改为“信息性”,认为这不是安全问题。
-
2天前: bagder 根据项目透明度政策请求披露此报告。
-
2天前: bagder 披露此报告。
报告详情
- 报告日期: 2025年12月27日,下午6:12 UTC
- 报告者: efrsxcv
- 报告对象: curl
- 报告ID: #3480039
- 状态: 信息性
- 严重性: 中等(4 ~ 6.9)
- 披露日期: 2025年12月28日,下午9:29 UTC
- 弱项类型: 业务逻辑错误
- CVE ID: 无
- 赏金: 无