libcurl Headers API 释放后重利用漏洞:跨请求复用头句柄导致堆内存安全风险

libcurl 释放后重用漏洞分析

漏洞概述

curl_easy_nextheader() API 返回的 struct curl_header 对象内部引用了 libcurl 所拥有的链表节点。当在同一个 CURL* 句柄上执行新请求时,libcurl 会释放并重建内部头列表,但先前返回的 struct curl_header 对象在应用程序中仍然有效,并且仍可作为参数被接受。

将此类过期的 header 对象传回 curl_easy_nextheader() 会导致 libcurl 解引用已释放的内部指针,从而引发堆释放后重用安全问题。

这是一个可通过正常 API 使用方式触发的内存安全漏洞,且受攻击者控制的 HTTP 响应影响。

影响版本

所有包含 Headers API(curl_easy_headercurl_easy_nextheader)的 libcurl 版本。

在 Kali Linux libcurl(2026 年 1 月版本)上复现成功,可能影响所有当前受支持的版本。

涉及文件:lib/headers.c

技术分析

漏洞设计缺陷

curl_easy_nextheader() 向应用程序暴露 struct curl_header 结构体:

c 复制代码
struct curl_header {
    const char *name;
    const char *value;
    unsigned int amount;
    unsigned int index;
    unsigned int origin;
    void *anchor;   /* 内部 Curl_llist_node */
};

anchor 字段存储指向 libcurl 内部链表节点的指针:

c 复制代码
copy_header_external(..., struct Curl_llist_node *e, ...) {
    h->anchor = e;
}

当执行新请求时,libcurl 释放内部头列表:

c 复制代码
Curl_headers_cleanup(struct Curl_easy *data) {
    for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) {
        struct Curl_header_store *hs = Curl_node_elem(e);
        curlx_free(hs);
    }
}

然而,先前返回的 struct curl_header 对象在用户代码中仍然保留,并仍可被传回:

c 复制代码
curl_easy_nextheader(CURL *easy, unsigned int type, int request,
                     struct curl_header *prev)

该函数直接解引用:

c 复制代码
pick = prev->anchor;
pick = Curl_node_next(pick);

如果头列表已被释放并重建,prev->anchor 现在指向已释放的堆内存 → 触发释放后重用。

存在的问题

  • 无旧 struct curl_header 失效机制
  • 无代际追踪
  • 无生命周期强制约束
  • API 文档未说明 header 句柄在新请求后会失效

概念验证代码

c 复制代码
#include <stdio.h>
#include <curl/curl.h>

int main(void) {
    CURL *curl = curl_easy_init();
    if(!curl) return 1;

    struct curl_header *h = NULL;

    /* 第一次请求 */
    curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_perform(curl);

    /* 获取并存储一个 header */
    h = curl_easy_nextheader(curl, CURLH_HEADER, -1, NULL);
    printf("saved header: %s: %s\n", h->name, h->value);

    /* 第二次请求(释放并重建内部头列表) */
    curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/get");
    curl_easy_perform(curl);

    /* 复用过期的 header 句柄 */
    printf("calling nextheader with stale prev...\n");
    struct curl_header *h2 = curl_easy_nextheader(curl, CURLH_HEADER, -1, h);
    if(h2)
        printf("next header: %s: %s\n", h2->name, h2->value);

    curl_easy_cleanup(curl);
    return 0;
}

编译与运行

bash 复制代码
gcc -fsanitize=address -g poc.c -lcurl -o poc
./poc

ASAN 输出(节选)

vbnet 复制代码
ERROR: AddressSanitizer: heap-use-after-free
READ of size 13
freed by:
    libcurl.so Curl_headers_cleanup
allocated by:
    libcurl.so Curl_headers_push

这确认了 curl_easy_nextheader() 解引用了已释放的内存。

安全影响

安全影响评估

恶意 HTTP 服务器可通过控制响应头,在使用 libcurl Headers API 且在同一个 CURL* 句柄上执行多个请求的应用程序中触发堆释放后重用。

可能导致:

  • 进程崩溃(拒绝服务)
  • 堆内存损坏
  • 根据分配器状态和周边内存布局,可能实现代码执行

漏洞特点:

  • 可通过公开 API 文档中的正常方式触达
  • 由攻击者控制的 HTTP 响应触发
  • 不需要中间人攻击

官方回应摘要

libcurl 团队认为此问题主要属于文档改进范畴,不视为安全漏洞。核心论点是:应用程序会在开发阶段立即崩溃,因此不会有实际部署的代码存在此问题。

官方已提交 PR 更新文档,说明指针在新传输后失效。该报告最终以"信息性"状态关闭,并已公开披露。 biOK/hzhVF2yKaGc5mK8oeejIYuUYW8I3RsXQCFCiXXHMxxsuKg0+t9iC0qHOQJ5

相关推荐
踩蚂蚁1 小时前
自定义语音唤醒词:从训练到部署的完整链路实践
人工智能
用户5191495848451 小时前
CVE-2025-1094 PostgreSQL SQL注入与WebSocket劫持远程代码执行利用工具
人工智能·aigc
IT_陈寒2 小时前
SpringBoot自动配置这个坑,我踩进去又爬出来了
前端·人工智能·后端
冬奇Lab14 小时前
Agent 系列(23):Web Agent——让 Agent 真正浏览网页
人工智能·llm·agent
冬奇Lab14 小时前
每日一个开源项目(第135篇):codebase-memory-mcp - 给 AI Agent 一张代码库的知识图谱
人工智能·开源·llm
IT_陈寒16 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
jooloo20 小时前
Codex 间歇性 400 之谜:一条对话里,它为什么有时候用 chat/completions,有时候切到 responses?
人工智能