curl -H 空格前缀导致代理模式下头部注入漏洞
漏洞概述
在curl 8.11.0版本中,当使用--proxy
参数时,-H
选项如果包含前导空格,会导致HTTP头部被注入到前一个头部中,而不是作为新的独立头部发送。
环境信息
- 操作系统:macOS Sequoia 15.1
- curl版本:8.11.0 (aarch64-apple-darwin24.1.0)
- 发布日期:2024-11-06
复现步骤
基本复现案例
1
|
curl -X GET "https://example.com" -H "Secure-Header: XYZ" -H " new-header-that-will-inject-to-previous-header: value" --proxy 127.0.0.1:8080
|
结果分析
当第一个头部本身包含空格时,行为会再次附加到前一个头部:
1
|
curl -X GET "https://example.com" -H " new-header-that-will-inject-to-previous-header: value" --proxy 127.0.0.1:8080
|
正常行为对比
不使用代理参数时,行为正常,头部会以新行发送:
1
|
curl -X GET "https://example.com" -H " new-header-that-will-inject-to-previous-header: value"
|
文件输入方式
该漏洞同样适用于-H @file.txt
方式,当文件内容以空格开头时也会触发相同行为。
安全影响
虽然目前没有具体的利用场景,但由于curl的广泛使用,可以想象在某些情况下@file.txt
或单个头部名称输入可能被攻击者控制,从而导致向服务器发送格式错误的请求。
示例攻击向量
1
|
curl -X GET "https://example.com" -H " new-header-that-will-inject-to-previous-header: value" -H @headers2.txt -H 'User-Agent:' -H 'Accept:' --proxy 127.0.0.1:8080
|
在这种情况下,头部会直接附加到Host头部,可能导致:
技术细节
限制条件
- 该漏洞仅在使用HTTPS请求时有效
- 仅在使用
--proxy
参数时触发
头部格式要求
-H "[space]abc"
:不会被传递
-H "[space]abc:abc"
:会被传递并触发漏洞
解决方案建议
由于HTTP头部字段名不能包含空格,建议的处理方式是:
- 去除前导空格
- 始终将头部添加到新行
官方回应
curl开发团队认为这是文档记录的行为。curl手册页对-H
选项的说明指出:
“curl传递您给出的逐字字符串,没有任何过滤或其他安全保护。这包括空格和控制字符。”
在HTTP/1中,可以通过代理传递"折叠"的头部进行连接,这是一个特性而非漏洞。
测试验证
通过自定义Python代理服务器进行测试,确认该行为确实存在:
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
|
#!/usr/bin/env python3
import socket
import threading
PROXY_HOST = '127.0.0.1'
PROXY_PORT = 8081
def handle_client(client_socket):
request = client_socket.recv(4096)
print("=== HTTP Request Received ===")
print(request.decode('utf-8', errors='replace'))
print("=============================")
client_socket.close()
def start_proxy():
proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxy_socket.bind((PROXY_HOST, PROXY_PORT))
proxy_socket.listen(5)
print(f"Proxy server running on {PROXY_HOST}:{PROXY_PORT}...")
while True:
client_socket, addr = proxy_socket.accept()
print(f"Connection received from {addr}")
client_handler = threading.Thread(target=handle_client, args=(client_socket,))
client_handler.start()
if __name__ == "__main__":
try:
start_proxy()
except KeyboardInterrupt:
print("\nShutting down the proxy server.")
|
漏洞状态
- 报告状态:不适用(Not Applicable)
- 严重性:中等(4 ~ 6.9)
- CVE ID:无
- 弱点类型:异常条件的不当检查或处理