Curl NTLM认证内存泄漏漏洞分析与修复

本文详细分析了Curl库中NTLM认证机制存在的内存泄漏漏洞,当处理大型NTLMv2 TargetInfo时,在特定条件下未能正确释放内存,可能导致客户端资源耗尽。文章包含完整的漏洞复现步骤和修复方案。

漏洞概述

在处理NTLMv2认证时,如果解码后的type-2 “TargetInfo"足够大,导致ntresplen+header_size超过NTLM_BUFSIZE(1024),代码会提前返回而未释放ntlmv2resp,造成内存泄漏。

安全影响:低 恶意对等方可以设置超过阈值的TargetInfo,导致客户端内存泄漏。通过重复连接,可能逐渐耗尽客户端资源。

该漏洞使用Theori的Xint Code(AI驱动工具)发现,但经过人工研究人员验证和确认。

受影响版本

影响最新版本8.16.0

复现步骤

使用curl模糊测试器作为问题复现方法,但在实际环境中可能由恶意HTTP服务器触发。

构建curl模糊测试器:

1
2
git clone https://github.com/curl/curl-fuzzers.git
cd curl-fuzzers && ./mainline.sh

生成PoC输入:

1
python poc.py # 输出 ./input.bin

在生成的输入上运行模糊测试器:

1
./build/curl_fuzzer_http input.bin

内存泄漏检测结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
=================================================================
==2757954==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 964 byte(s) in 1 object(s) allocated from:
    #0 0x563de804999d in calloc
    #1 0x563de80e4a90 in curl_dbg_calloc
    #2 0x563de84520ca in Curl_ntlm_core_mk_ntlmv2_resp
    #3 0x563de83e6420 in Curl_auth_create_ntlm_type3_message
    #4 0x563de8357698 in Curl_output_ntlm
    #5 0x563de82ed9f2 in output_auth_headers http.c
    #6 0x563de82ed30f in Curl_http_output_auth
    #7 0x563de82e3afd in Curl_http
    #8 0x563de812b460 in multi_do multi.c
    #9 0x563de81288a9 in state_do multi.c
    #10 0x563de8116b00 in multi_runsingle multi.c
    #11 0x563de8114894 in curl_multi_perform
    #12 0x563de808c3f7 in fuzz_handle_transfer(fuzz_data*)
    #13 0x563de808a70d in LLVMFuzzerTestOneInput
    #14 0x563de9487ff9 in main
    #15 0x7f5e7782a1c9 in __libc_start_main
    #16 0x7f5e7782a28a in __libc_start_main
    #17 0x563de7fae964 in _start

修复方案

开发人员提供了初始补丁尝试:

 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
diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c
index 791fc87d11..d860fbbd50 100644
--- a/lib/vauth/ntlm.c
+++ b/lib/vauth/ntlm.c
@@ -786,35 +786,35 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
   });
 
   /* ntresplen + size should not be risking an integer overflow here */
   if(ntresplen + size > sizeof(ntlmbuf)) {
     failf(data, "incoming NTLM message too big");
-    return CURLE_OUT_OF_MEMORY;
+    result = CURLE_TOO_LARGE;
+    goto error;
   }
   DEBUGASSERT(size == (size_t)ntrespoff);
   memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen);
   size += ntresplen;
 
   DEBUG_OUT({
     curl_mfprintf(stderr, "\n   ntresp=");
     ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen);
   });
 
-  free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */
-
   DEBUG_OUT({
     curl_mfprintf(stderr, "\n   flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ",
                   LONGQUARTET(ntlm->flags), ntlm->flags);
     ntlm_print_flags(stderr, ntlm->flags);
     curl_mfprintf(stderr, "\n****\n");
   });
 
   /* Make sure that the domain, user and host strings fit in the
      buffer before we copy them there. */
   if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) {
-    failf(data, "user + domain + hostname too big");
-    return CURLE_OUT_OF_MEMORY;
+    failf(data, "user + domain + hostname too big for NTLM");
+    result = CURLE_TOO_LARGE;
+    goto error;
   }
 
   DEBUGASSERT(size == domoff);
   if(unicode)
     unicodecpy(&ntlmbuf[size], domain, domlen / 2);
@@ -840,10 +840,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
   size += hostlen;
 
   /* Return the binary blob. */
   result = Curl_bufref_memdup(out, ntlmbuf, size);
 
+error:
+  free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */
+
   Curl_auth_cleanup_ntlm(ntlm);
 
   return result;
 }

技术讨论

最大泄漏大小:通过将target_info_len设置为65535,可以触发最大65KB的内存泄漏。

安全影响评估:由于此泄漏实际上需要中间人攻击才能发生,而此类攻击者具有比触发每次认证错误64K泄漏更大的破坏潜力,因此被视为"只是一个错误"而非安全漏洞。

修复状态:该错误已在git中合并,将包含在下一个版本8.17.0中。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计