概括:
对于每个传入PUSH_PROMISE标头,都会分配一个新name:value字符串,并将指向该字符串的指针存储在stream->push_headers数组中。
h = aprintf("%s:%s", name, value);
if(h)
stream->push_headers[stream->push_headers_used++] = h;
Libcurl 将拒绝PUSH_PROMISE带有太多标头的帧。当标头数量超过某个阈值时,on_header返回错误。然而,libcurl 忘记在释放之前释放stream->push_headers数组元素。stream->push_headers恶意服务器可能会连续发送PUSH_PROMISE具有超过 1000 个标头的帧,这最终会耗尽所有可用内存。
失败时也存在同样的问题Curl_saferealloc。
if(stream->push_headers_alloc > 1000) {
/* this is beyond crazy many headers, bail out */
failf(data_s, "Too many PUSH_PROMISE headers");
Curl_safefree(stream->push_headers);
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
stream->push_headers_alloc *= 2;
headp = Curl_saferealloc(stream->push_headers,
stream->push_headers_alloc * sizeof(char *));
if(!headp) {
stream->push_headers = NULL;
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
重现步骤:
-
compile nghttp_v1.59.patch (F 3099659 )nghttp2进行编译
-
compile http2_push_promise.c (F 3099658 )
-
run nghttpd -p/=/foo.bar --no-tls 8181
-
run valgrind --leak-check=full http2_push_promise
每个-p 选项 nghttpd 将发送 200 个PUSH_PROMISE帧,每个帧有 1280 个标头(不包括伪标头)
支持材料/参考文献:
valgrind --leak-check=full http2_push_promise 输出:
==13928==
==13928== HEAP SUMMARY:
==13928== in use at exit: 8,285,018 bytes in 256,674 blocks
==13928== total heap usage: 261,567 allocs, 4,893 frees, 12,766,009 bytes allocated
==13928==
==13928== 64 bytes in 2 blocks are possibly lost in loss record 2 of 10
==13928== at 0x48436C4: malloc (vg_replace_malloc.c:392)
==13928== by 0x4889F45: dyn_nappend (dynbuf.c:107)
==13928== by 0x488A2C5: Curl_dyn_addn (dynbuf.c:170)
==13928== by 0x48C393E: alloc_addbyter (mprintf.c:1065)
==13928== by 0x48C2FF9: dprintf_formatf (mprintf.c:852)
==13928== by 0x48C39FF: curl_mvaprintf (mprintf.c:1095)
==13928== by 0x48C3AF0: curl_maprintf (mprintf.c:1110)
==13928== by 0x48B0F86: on_header (http2.c:1467)
==13928== by 0x4C310C1: nghttp2_session_mem_recv (in /usr/lib64/libnghttp2.so.14.25.1)
==13928== by 0x48AE62B: h2_process_pending_input (http2.c:552)
==13928== by 0x48B2570: h2_progress_ingress (http2.c:1914)
==13928== by 0x48B2775: cf_h2_recv (http2.c:1953)
==13928==
==13928== 8,191,872 bytes in 255,996 blocks are definitely lost in loss record 10 of 10
==13928== at 0x48436C4: malloc (vg_replace_malloc.c:392)
==13928== by 0x4889F45: dyn_nappend (dynbuf.c:107)
==13928== by 0x488A2C5: Curl_dyn_addn (dynbuf.c:170)
==13928== by 0x48C393E: alloc_addbyter (mprintf.c:1065)
==13928== by 0x48C2FF9: dprintf_formatf (mprintf.c:852)
==13928== by 0x48C39FF: curl_mvaprintf (mprintf.c:1095)
==13928== by 0x48C3AF0: curl_maprintf (mprintf.c:1110)
==13928== by 0x48B0F86: on_header (http2.c:1467)
==13928== by 0x4C310C1: nghttp2_session_mem_recv (in /usr/lib64/libnghttp2.so.14.25.1)
==13928== by 0x48AE62B: h2_process_pending_input (http2.c:552)
==13928== by 0x48B2570: h2_progress_ingress (http2.c:1914)
==13928== by 0x48B2775: cf_h2_recv (http2.c:1953)
==13928==
==13928== LEAK SUMMARY:
==13928== definitely lost: 8,191,872 bytes in 255,996 blocks
==13928== indirectly lost: 0 bytes in 0 blocks
==13928== possibly lost: 64 bytes in 2 blocks
==13928== still reachable: 93,082 bytes in 676 blocks
==13928== suppressed: 0 bytes in 0 blocks
==13928== Reachable blocks (those to which a pointer was found) are not shown.
==13928== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==13928==
==13928== For lists of detected and suppressed errors, rerun with: -s
==13928== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
影响
拒绝服务-原文报告带有两个附件,可以阅读原文,查询详细修复内容。
这基本上需要恶意服务器来触发,因为没有理智的服务器会发送过多的标头。
缓解因素:应用程序需要接受推送请求才能实现这一点。在现实世界中,200 个恶意请求似乎不太可能真正被接受。
但可以肯定的是,即使“仅仅”1000 个巨大的标头也会造成内存泄漏。
原文地址:
https://hackerone.com/reports/2402845
原文始发于微信公众号(Ots安全):CVE-2024-2398:HTTP/2 推送标头内存泄漏
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论