curl Use-After-Free漏洞(导致某些版本任意写入) | HackerOne
摘要
Use-After-Free漏洞导致任意写入/读取
是的,我使用了IA和mermaid编辑器(在线版)生成此图表,显示(分配、释放和释放后使用)的路径: bug_svg.png (F4637660): bug_svg.png
受影响版本
curl 8.13.0 (x86_64-pc-linux-gnu) libcurl/8.13.0 OpenSSL/3.5.0 zlib/1.3.1 brotli/1.1.0 zstd/1.5.5 libpsl/0.21.2(任意写入/读取)
asan_crash_log_curl_8.13.0-Arbitrary-READ.log (F4637640): asan_crash_log_curl_8.13.0-Arbitrary-READ.log asan_crash_log_curl_8.13.0-Arbitrary-WRITE.log (F4637641): asan_crash_log_curl_8.13.0-Arbitrary-WRITE.log
curl 8.14.0 (x86_64-pc-linux-gnu) libcurl/8.14.0 OpenSSL/3.5.0 zlib/1.3.1 brotli/1.1.0 zstd/1.5.5 libpsl/0.21.2(任意写入/读取)
asan_crash_log_curl_8.14.0-Arbitrary-READ.log (F4637642): asan_crash_log_curl_8.14.0-Arbitrary-READ.log asan_crash_log_curl_8.14.0-Arbitrary-WRITE.log (F4637643): asan_crash_log_curl_8.14.0-Arbitrary-WRITE.log
curl 8.15.0 (x86_64-pc-linux-gnu) libcurl/8.15.0 OpenSSL/3.5.0 zlib/1.3.1 brotli/1.1.0 zstd/1.5.5 libpsl/0.21.2(仅任意写入)
asan_crash_log_curl_8.15.0-Arbitrary-WRITE.log (F4637644): asan_crash_log_curl_8.15.0-Arbitrary-WRITE.log
=> 测试环境:
上述所有版本均在Linux kali 6.3.0-kali1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.3.7-1kali1上测试。 Ubuntu 24.04.1(仅测试了curl版本8.14.0) 我使用curl版本8.14.0调查此问题的根本原因,因此当我引用某些函数时,行号可能略有变化。
复现步骤
从github下载目标版本并解压:
|
|
构建并安装:
|
|
|
|
|
|
请参考崩溃文件asan_crash_log_curl_8.14.0-Arbitrary-WRITE.log (F4637643): asan_crash_log_curl_8.14.0-Arbitrary-WRITE.log 如果想在崩溃时停止循环,请使用:
|
|
也可以使用配置文件,编写配置文件(conf.txt):
|
|
然后执行:
|
|
检测到崩溃时停止循环:
|
|
崩溃分析
对于崩溃分析,我主要关注ASAN输出以及手动代码审查,核心转储文件也很有帮助(无法上传,因为大小为830 MB)。
启用核心转储(基于Debian的系统):
|
|
主要传输循环
|
|
当curl以并行模式调用时,使用curl_multi_perform()函数。
如崩溃输出所示,当调用curl_easy_init()函数时,它使用Curl_open(&data)函数分配Curl_easy结构(data = calloc(1, sizeof(struct Curl_easy));),然后将其分配给传递给Curl_open()的&data。
|
|
传输完成后,在check_finished(s)中调用post_per_transfer()函数,该函数调用curl_easy_cleanup(),进而调用Curl_close(&data),该函数使用Curl_safefree()、Curl_freeset()等释放数据组件并空其指针,然后释放数据本身,从而释放整个struct Curl_easy数据,包括其state.timenode(struct Curl_tree timenode)。
|
|
在multi.c:3474,Curl_expire_clear()函数调用Curl_splayremove(multi->timetree, &data->state.timenode,&multi->timetree)`,传递相同的指针(&data->state.timenode)作为第二个参数,该指针已被释放。 在/splay.c:234,Curl_splayremove()函数进行指针操作(假设传递的removenode是树中的有效节点),这写入已释放的堆内存(释放后使用->任意写入)。
|
|
说明
由于这些括号范围"[1-10]“纯粹是curl命令行(和配置文件)的功能(称为URL globbing),并且这些类型的URL不受libcurl API支持,因此当curl二进制文件或libcurl API解析括号范围URL(将范围扩展为数组)然后在并行模式(-Z)下与curl_multi_perform、curl_multi_poll等一起使用时,会出现此错误。 当使用–max-time选项设置为相对较低的值(0.01与[1-10];0.1与URL中的[1-100]范围也会触发错误)时,会触发崩溃。 我相信我们可以使用libcurl API重现此问题并到达易受攻击的路径,我尝试使用10-at-a-time.c,编辑URL数组以通过–libcurl选项解析,并添加其他选项(如CURLOPT_NOPROGRESS用于–silent和(easy_handle,CURLOPT_TIMEOUT_MS,10L)用于max-time 0.01),但它没有工作。因为curl命令中–max-time的行为应用于整个URL(带括号范围),因此需要更多工作。 为清晰起见,我尚未使用C代码(使用libcurl <curl/curl.h>)触发相同的崩溃。 此错误仅在启用Address Sanitizers时触发,并且不一定导致应用程序崩溃,未启用ASAN时,它仅损坏堆(curl版本8.15.0)并中止(curl版本8.14.0),这可能导致段错误,当运行更多几次迭代时可能崩溃应用程序,这可能使攻击完美,因为正常的curl操作没有明显指标(但调试相当具有挑战性,因为启用asan时无法使用gdb和strace) 此错误还可以通过其他功能触发,例如–append (-a)、–upload (-T)。 因此,我建议将此错误评为高严重性。 仍在尝试找出可靠的方法来利用此错误。
支持材料/参考
CWE-416: Use After Free: https://cwe.mitre.org/data/definitions/416.html CWE-123: Write-what-where Condition: https://cwe.mitre.org/data/definitions/123.html https://curl.se/libcurl/c/curl_multi_perform.html https://everything.curl.dev/cmdline/urls/globbing.html https://curl.se/libcurl/c/10-at-a-time.html https://www.youtube.com/watch?v=YV3jewkUJ54
影响
此错误的可靠利用允许控制服务器的攻击者在受害者系统上执行任意命令。 执行也可以通过一些注入技术实现。 如果与另一个漏洞链式利用,允许控制传递给curl/libcurl的URL,并劫持服务器响应以进行堆整形,则可能更具影响力。 堆风水(heap feng shui)和堆修饰(heap Grooming)技术可用于覆盖目标地址并在受害者机器上执行命令(仍在调查中)。
附件
6个附件 F4637640: asan_crash_log_curl_8.13.0-Arbitrary-READ.log F4637641: asan_crash_log_curl_8.13.0-Arbitrary-WRITE.log F4637642: asan_crash_log_curl_8.14.0-Arbitrary-READ.log F4637643: asan_crash_log_curl_8.14.0-Arbitrary-WRITE.log F4637644: asan_crash_log_curl_8.15.0-Arbitrary-WRITE.log F4637660: bug_svg.png
活动时间线
letshack9707 向curl提交报告。10天前
dfandrich curl staff 发表评论。10天前 c071993_domain22.com是一个无效的DNS主机名,因为包含下划线。是否有公开服务器显示此内容?
jimfuller2024 curl staff 发表评论。10天前 能够在8.15.0上重现 完成 134 1500920ERROR: AddressSanitizer: heap-use-after-free on address 0x7dd814c566d8 at pc 0x7fb816edc32e bp 0x7ffc3ce20540 sp 0x7ffc3ce20538 WRITE of size 8 at 0x7dd814c566d8 thread T0 #0 0x7fb816edc32d in Curl_splayremove /home/jfuller/src/curl/lib/splay.c:234 #1 0x7fb816e425bb in Curl_expire_clear /home/jfuller/src/curl/lib/multi.c:3598 #2 0x7fb816e1c81c in init_completed /home/jfuller/src/curl/lib/multi.c:131 #3 0x7fb816e1d231 in mstate /home/jfuller/src/curl/lib/multi.c:189 #4 0x7fb816e35e7f in multi_runsingle /home/jfuller/src/curl/lib/multi.c:2645 #5 0x7fb816e382c3 in curl_multi_perform /home/jfuller/src/curl/lib/multi.c:2739 #6 0x000000467f54 in parallel_transfers /home/jfuller/src/curl/src/tool_operate.c:1921 #7 0x00000046a289 in run_all_transfers /home/jfuller/src/curl/src/tool_operate.c:2215 #8 0x00000046aef2 in operate /home/jfuller/src/curl/src/tool_operate.c:2357 #9 0x000000452f69 in main /home/jfuller/src/curl/src/tool_main.c:275 #10 0x7fb815a115f4 in libc_start_call_main (/lib64/libc.so.6+0x35f4) (BuildId: c4b06a608071b2c9852fae62ca3b69cdc22cd022) #11 0x7fb815a116a7 in libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x36a7) (BuildId: c4b06a608071b2c9852fae62ca3b69cdc22cd022) #12 0x000000400e44 in _start (/usr/local/bin/curl+0x400e44) (BuildId: 3154567858150bf133b755149bdffd2f554f7f69) 0x7dd814c566d8 is located 3544 bytes inside of 5392-byte region [0x7dd814c55900,0x7dd814c56e10) freed by thread T0 here: #0 0x7fb817ee5bcb in free.part.0 (/lib64/libasan.so.8+0xe5bcb) (BuildId: 7f1aa7e2e600e8c9d54ce6