curl库telnet协议处理器中的指针下溢漏洞分析

本文详细分析了curl库lib/telnet.c文件中存在的telnet子选项缓冲区指针下溢漏洞。该漏洞在处理特定telnet协议数据时,由于无条件指针递减操作导致越界读取,可能造成信息泄露。

Telnet子选项缓冲区指针下溢导致越界读取漏洞分析

漏洞概述

在curl的telnet协议处理器(lib/telnet.c)中存在缓冲区指针下溢漏洞。当在CURL_TS_SE状态处理telnet子选项时,代码无条件地将子选项缓冲区指针递减2(subpointer -= 2),即使CURL_SB_ACCUM宏因缓冲区已满而跳过写入操作。这导致随后调用suboption()printsub()时发生越界读取。

受影响版本

  • 所有包含此代码模式且支持telnet的curl版本
  • 已在GitHub上最新的curl源代码(master分支)测试
  • 文件:lib/telnet.c
  • 易受攻击的行:1210和1226

技术分析

易受攻击的代码模式

在lib/telnet.c中,CURL_SB_ACCUM宏(第69-73行)有条件地写入缓冲区:

1
2
3
4
5
#define CURL_SB_ACCUM(x, c)                                   \
  do {                                                        \
    if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \
      *x->subpointer++ = (c);                                 \
  } while(0)

然而,在CURL_TS_SE状态处理(第1207-1211行和第1223-1227行)中,指针递减是无条件的:

路径1(第1207-1211行):

1
2
3
4
CURL_SB_ACCUM(tn, CURL_IAC);
CURL_SB_ACCUM(tn, c);
tn->subpointer -= 2;    // 无条件 - 缓冲区满时导致下溢
CURL_SB_TERM(tn);

路径2(第1223-1227行):

1
2
3
4
CURL_SB_ACCUM(tn, CURL_IAC);
CURL_SB_ACCUM(tn, CURL_SE);
tn->subpointer -= 2;    // 无条件 - 缓冲区满时导致下溢
CURL_SB_TERM(tn);

利用流程

  1. 恶意telnet服务器发送子选项数据以填满512字节缓冲区(SUBBUFSIZE)
  2. 缓冲区满时,服务器发送IAC后跟另一个字节
  3. CURL_SB_ACCUM宏不执行任何操作(缓冲区满检查通过)
  4. subpointer -= 2无条件执行,导致指针下溢
  5. CURL_SB_TERM设置subend = subpointer(现在指向缓冲区起始位置之前)
  6. 使用损坏的指针调用suboption()
  7. CURL_SB_LEN宏计算负/回绕长度
  8. 详细模式下printsub()读取越界内存

内存布局(struct TELNET)

1
2
3
4
5
6
7
8
struct TELNET {
  // ... 其他字段 ...
  struct dynbuf out;              // 包含堆指针
  unsigned char subbuffer[512];   // 子选项缓冲区
  unsigned char *subpointer;      // 指向subbuffer
  unsigned char *subend;          // 结束标记
  // ...
};

subpointer下溢时,它指向dynbuf out结构,该结构包含可能泄露的堆指针。

复现步骤

  1. 将附带的PoC脚本保存为curl_telnet_poc.py
  2. 运行恶意telnet服务器:
    1
    
    python3 curl_telnet_poc.py -p 2323
    
  3. 在另一个终端中,使用详细模式连接curl:
    1
    
    curl -v telnet://127.0.0.1:2323
    
  4. 观察详细输出中的以下内容:
    • 输出中的异常子选项数据
    • 指示内存泄露的潜在不可打印字符
    • 可能的崩溃(取决于内存布局)

为了更好地观察,使用AddressSanitizer构建curl:

1
2
3
./configure CFLAGS="-fsanitize=address -g"
make
./src/curl -v telnet://127.0.0.1:2323

ASan应报告越界读取。

支持材料/参考

  • 附件:curl_telnet_poc.py - Python PoC服务器脚本
  • 源文件:https://github.com/curl/curl/blob/master/lib/telnet.c
  • 易受攻击的行:1210, 1226
  • 相关宏:第63-76行(CURL_SB_CLEAR, CURL_SB_TERM, CURL_SB_ACCUM, CURL_SB_GET, CURL_SB_LEN)

影响

安全影响

  1. 信息泄露(低-中):当curl以详细模式(-v)运行时,printsub()函数可能读取并显示子选项缓冲区之前的内存内容。这可能潜在地泄露:

    • 堆指针(可用于链式利用中的ASLR绕过)
    • dynbuf out结构的内容
    • 相邻内存中的其他敏感数据
  2. 拒绝服务(低):根据内存布局和访问模式,越界读取可能导致崩溃。

攻击场景

控制恶意telnet服务器的攻击者可以:

  1. 等待受害者使用curl -v telnet://malicious-server连接
  2. 发送精心构造的telnet子选项数据以触发漏洞
  3. 可能观察错误消息或日志中泄露的内存内容
  4. 使用泄露的堆指针帮助利用其他漏洞

限制

  • 要求受害者连接到攻击者控制的telnet服务器
  • 在详细模式(-v标志)下影响最大
  • Telnet协议已弃用且很少使用
  • 无直接代码执行能力

漏洞确认与讨论

测试输出作为证明

测试输出中显示的内容证实了漏洞的存在:

1
2
RCVD IAC SB
(terminated by 65 65, not IAC SE)

这是漏洞的表现。代码期望在终止符位置找到IAC SE(字节255, 240),但找到了65 65(ASCII ‘A’ ‘A’ - 填充字节)。这是因为CURL_SB_LEN由于无条件的subpointer -= 2返回了不正确的值。

为何ASAN不触发

ASAN检测越界内存访问,而非逻辑错误。此处:

  • printsub()读取subbuffer[510]subbuffer[511] - 仍在512字节缓冲区内
  • 错误是长度损坏,而非内存损坏
  • 数据从有效内存读取,但在错误的偏移处

技术证明

正常流程(缓冲区未满):

  • CURL_SB_ACCUM(IAC) → 写入字节,subpointer++(现在在N+1)
  • CURL_SB_ACCUM(SE) → 写入字节,subpointer++(现在在N+2)
  • subpointer -= 2 → 回到N
  • CURL_SB_LEN = N(正确)

错误流程(缓冲区满在512):

  • CURL_SB_ACCUM(IAC) → 缓冲区满检查失败,无写入,无递增(保持在512)
  • CURL_SB_ACCUM(SE) → 缓冲区满检查失败,无写入,无递增(保持在512)
  • subpointer -= 2 → 现在在510(但实际有512字节数据!)
  • CURL_SB_LEN = 510(应为512)

结果:printsub()以长度512调用,但在位置510而非IAC SE应在的位置寻找终止符。它找到的是’A’ ‘A’。

建议修复

1
2
3
4
5
6
7
8
// 在ACCUM调用之前保存指针
unsigned char *sb_start = tn->subpointer;
CURL_SB_ACCUM(tn, CURL_IAC);
CURL_SB_ACCUM(tn, CURL_SE);
// 仅当两个字节实际写入时才递减
if(tn->subpointer == sb_start + 2)
    tn->subpointer -= 2;
CURL_SB_TERM(tn);

应用于两个位置:第1208-1211行和第1223-1227行。

影响评估

同意严重性为低:

  • 需要连接到恶意telnet服务器
  • 主要在详细模式下可见
  • Telnet已弃用
  • 无直接代码执行能力

但这是一个有效的错误,导致:

  • 不正确的长度计算
  • 错误的终止符检测(由测试证明)
  • 子选项处理中的潜在逻辑错误

项目方响应与修复

curl团队确认了该问题但将其归类为"信息性"而非安全漏洞,因为信息泄露仅发生在详细输出中。他们提出了修复方案,即为此错误条件返回错误而非尝试继续处理,并提交了相关PR(https://github.com/curl/curl/pull/20108)。

披露

根据项目透明政策,该报告已公开披露。

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