cURL AmigaOS堆缓冲区溢出漏洞深度剖析
漏洞概述
在cURL的AmigaOS特定主机名解析函数 Curl_ipv4_resolve_r 中存在一个基于堆的缓冲区溢出漏洞。该漏洞允许远程攻击者通过提供特制的主机名或DNS响应来破坏堆内存,当cURL处理恶意输入时,可能导致远程代码执行。
技术细节
漏洞代码位置
文件: curl/lib/amigaos.c
函数: Curl_ipv4_resolve_r (约第123-173行)
根本原因分析
漏洞发生在gethostbyname_r函数的缓冲区大小计算中:
1
2
3
4
5
6
7
|
buf = curlx_calloc(1, CURL_HOSTENT_SIZE); // 1. 固定大小分配
if(buf) {
h = gethostbyname_r((STRPTR)hostname, buf,
(char *)buf + sizeof(struct hostent), // 2. 错误的缓冲区偏移量
CURL_HOSTENT_SIZE - sizeof(struct hostent), // 3. 错误的大小计算
&h_errnop);
}
|
缺陷剖析
三个关键问题共同导致了溢出:
-
固定缓冲区大小 (CURL_HOSTENT_SIZE):
- 在典型配置中定义为8192
- 未考虑实际的DNS响应大小
- 假设所有主机名解析都能适应此限制
-
错误的指针算术运算:
1
|
(char *)buf + sizeof(struct hostent)
|
- 假设
struct hostent可以紧密打包
- 忽略对齐要求(结构体填充)
- 实际偏移应为:
ALIGN_UP(sizeof(struct hostent), alignof(max_align_t))
-
错误的大小计算:
1
|
CURL_HOSTENT_SIZE - sizeof(struct hostent)
|
- 未考虑对齐填充
- 在某些平台上可能导致负数/下溢
- 没有为元数据存储预留安全边际
内存布局利用
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
|
调用前的堆布局:
+-------------------+-------------------+-------------------+
| buf (8192 bytes) | 下一个堆块 | 后续堆分配 |
| | 元数据/数据 | |
+-------------------+-------------------+-------------------+
^ ^
0x1000 0x3000 (示例)
gethostbyname_r期望的布局:
+-------------------+-------------------+
| struct hostent | 数据缓冲区 | (连续)
+-------------------+-------------------+
^ ^
buf buf + sizeof(struct hostent)
实际发生的情况(考虑对齐):
+-------------------+-------------------+-------------------+
| struct hostent | 填充 (4-8字节) | 数据缓冲区 |
| (56字节) | | |
+-------------------+-------------------+-------------------+
^ ^
buf buf + sizeof(struct hostent) + padding
│
└──► 实际数据从此处开始,
但代码假设从更早的位置开始
导致缓冲区大小不足!
|
利用场景
远程攻击向量
1
2
3
4
5
6
7
8
9
10
11
12
13
|
攻击者控制的DNS服务器
↓
恶意DNS响应
↓
cURL DNS解析
↓
gethostbyname_r处理超大响应
↓
amigaos.c中的堆缓冲区溢出
↓
受控的堆破坏
↓
远程代码执行/拒绝服务/信息泄露
|
概念验证(PoC)
1
2
3
4
5
6
7
8
9
10
11
|
# 设置恶意DNS服务器:
# - 主机名:4000+字符的主机名
# - 多个CNAME记录(链式50+)
# - 多个A记录(100+ IP)
# - 附加部分中的长TXT记录
# 触发方式:
curl http://$(python3 -c "print('A'*4000 + '.evil.com')")/
# 或通过重定向:
echo "HTTP/1.1 302 Found\nLocation: http://${LONG_HOSTNAME}/\n" | nc -l 8080
|
攻击前提条件
- 对使用cURL的目标具有网络访问权限
- 能够控制DNS响应或主机名输入
- 具有易受攻击的cURL构建的AmigaOS系统
- 有利于利用的堆布局
影响评估
直接影响
- 远程代码执行: 通过堆元数据破坏导致任意写入原语
- 拒绝服务: 通过堆破坏导致cURL或调用应用程序崩溃
- 信息泄露: 读取包含敏感数据的相邻堆内存
- 权限提升: 如果cURL以提升的权限运行(setuid/setgid)
受影响的使用案例
- 使用用户提供URL的命令行cURL
- 解析不受信任主机名的libcurl应用程序
- 使用cURL进行上游请求的代理服务器
- 处理外部URL的自动化脚本
代码审查发现
其他相关问题
问题A:缺少输入验证
1
2
3
4
5
|
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port)
{
// 对hostname没有长度验证!
// 主机名可能为64KB,立即导致问题
}
|
问题B:线程安全竞争条件
1
2
3
4
|
#ifdef CURLRES_THREADED
struct Library *base = OpenLibrary("bsdsocket.library", 4);
// OpenLibrary与实际使用之间存在竞争条件
#endif
|
问题C:资源泄漏路径
1
2
3
4
5
6
7
|
if(ISocket) {
h = gethostbyname((STRPTR)hostname);
if(h) {
ai = Curl_he2ai(h, port); // 如果此步骤失败,资源不会被清理
}
// ... 清理仅当ISocket存在时发生
}
|
推荐修复方案
即时补丁
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
|
diff --git a/lib/amigaos.c b/lib/amigaos.c
index abc123..def456 100644
--- a/lib/amigaos.c
+++ b/lib/amigaos.c
@@ -150,6 +150,12 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port)
{
struct Curl_addrinfo *ai = NULL;
struct hostent *h;
+
+ // 输入验证
+ if(!hostname || strlen(hostname) > MAX_HOSTNAME_LEN) {
+ return NULL;
+ }
+
struct SocketIFace *ISocket = __CurlISocket;
if(SocketFeatures & HAVE_BSDSOCKET_GETHOSTBYNAME_R) {
@@ -157,10 +163,15 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port)
struct hostent *buf;
buf = curlx_calloc(1, CURL_HOSTENT_SIZE);
- if(buf) {
+ if(buf && hostname && *hostname) {
+ // 使用对齐计算安全的缓冲区大小
+ size_t hostent_size = sizeof(struct hostent);
+ size_t aligned_size = (hostent_size + 7) & ~7; // 8字节对齐
+ size_t data_size = CURL_HOSTENT_SIZE - aligned_size;
+
h = gethostbyname_r((STRPTR)hostname, buf,
- (char *)buf + sizeof(struct hostent),
- CURL_HOSTENT_SIZE - sizeof(struct hostent),
+ (char *)buf + aligned_size,
+ data_size,
&h_errnop);
if(h) {
ai = Curl_he2ai(h, port);
|
额外的安全措施
添加编译时断言:
1
2
|
_Static_assert(CURL_HOSTENT_SIZE > sizeof(struct hostent) * 4,
"CURL_HOSTENT_SIZE太小,无法安全操作");
|
实现运行时边界检查:
1
2
3
4
5
|
// 在gethostbyname_r调用之前
if(data_size < MIN_SAFE_DNS_BUFFER) {
curlx_free(buf);
return NULL;
}
|
使用安全替代方案:
1
2
3
4
|
// 优先使用getaddrinfo(如果可用)
#if defined(HAVE_GETADDRINFO)
return Curl_getaddrinfo(hostname, port);
#endif
|
影响总结
缓冲区溢出在cURL AmigaOS中
严重性:高 (CVSS 8.1)
主要影响: 远程代码执行
攻击向量: 网络可访问
前提条件: 用户/应用程序解析攻击者控制的主机名
结果: 完全破坏cURL进程内存空间
利用过程: 堆破坏 → 控制流劫持 → 任意代码执行
次要影响:
-
直接影响可用性
- 拒绝服务:可靠地使cURL进程崩溃
- 服务中断:破坏所有后续网络操作
- 资源耗尽:可能导致内存破坏级联
-
保密性影响
- 堆内存泄露:相邻内存可能包含:
- SSL/TLS会话密钥
- 认证令牌(Bearer、API密钥)
- HTTP cookies和会话数据
- 用户凭证
- 进程内存指针(ASLR绕过)
-
完整性影响
- 数据破坏:修改传输中的HTTP数据
- 请求操纵:更改传出的HTTP请求
- 响应篡改:修改接收的HTTP响应
- 配置覆盖:破坏cURL内部状态
受影响系统:
- AmigaOS 4.x(现代部署)
- AmigaOS 3.x(带有TCP/IP堆栈的遗留系统)
- AROS(开源兼容)
- MorphOS(PowerPC兼容)
- 任何使用带有易受攻击cURL的AmigaOS套接字库的系统
攻击面:
- 直接:
curl http://malicious-hostname/
- 间接:HTTP重定向、HTML嵌入、API调用
- 自动:软件更新、feed阅读器、同步客户端
- 持久性:本地hosts文件投毒
最坏情况场景:
远程、未认证的攻击者 → AmigaOS服务器上的RCE → 渗透到内部网络 → 数据泄露/加密货币挖矿/横向移动
厂商回应
报告提交后,cURL团队成员对报告提出了质疑:
- 质疑报告中关于缓冲区大小定义的准确性(指出实际为9000字节而非8192字节)
- 认为如果
gethostbyname_r()不尊重给定的缓冲区大小,这不是cURL的漏洞,而是解析器函数的漏洞
- 认为报告包含过多无关信息,不够精确清晰
- 最终将报告标记为"AI垃圾内容"并关闭报告,同时禁用了提交者账户
报告随后根据项目透明度政策被公开披露。