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 行)有条件地写入缓冲区:
|
|
然而,在 CURL_TS_SE 状态处理中(第 1207-1211 行和第 1223-1227 行),指针递减是无条件的:
路径 1(第 1207-1211 行):
|
|
路径 2(第 1223-1227 行):
|
|
利用流程
- 恶意 telnet 服务器发送子选项数据填满 512 字节缓冲区(
SUBBUFSIZE)。 - 缓冲区已满时,服务器发送 IAC 后跟另一个字节。
CURL_SB_ACCUM宏不执行任何操作(缓冲区已满检查失败)。subpointer -= 2无条件执行,导致指针下溢。CURL_SB_TERM设置subend = subpointer(现在指向缓冲区起始位置之前)。- 调用
suboption(),参数为损坏的指针。 CURL_SB_LEN宏计算出负数/回绕的长度。- 在详细模式下,
printsub()读取越界内存。
内存布局(结构体 TELNET)
|
|
当 subpointer 下溢时,它指向 dynbuf out 结构体,该结构体包含可能泄露的堆指针。
复现步骤
- 将附带的 PoC 脚本保存为
curl_telnet_poc.py。 - 运行恶意 telnet 服务器:
1python3 curl_telnet_poc.py -p 2323 - 在另一个终端中,使用详细模式连接 curl:
1curl -v telnet://127.0.0.1:2323 - 观察详细输出中是否包含:
- 输出中的异常子选项数据
- 表示内存泄露的潜在非打印字符
- 可能的崩溃(取决于内存布局)
为了更好地观察,可以使用 AddressSanitizer 构建 curl:
|
|
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)
影响
安全影响
- 信息泄露(低-中):当 curl 以详细模式(
-v)运行时,printsub()函数可能会读取并显示子选项缓冲区之前的内存内容。这可能泄露:- 堆指针(可用于链式攻击中的 ASLR 绕过)
dynbuf out结构体的内容- 相邻内存中的其他敏感数据
- 拒绝服务(低):根据内存布局和访问模式,越界读取可能导致崩溃。
攻击场景
控制恶意 telnet 服务器的攻击者可以:
- 等待受害者使用
curl -v telnet://malicious-server连接。 - 发送精心构造的 telnet 子选项数据以触发漏洞。
- 可能观察错误消息或日志中泄露的内存内容。
- 使用泄露的堆指针来帮助利用其他漏洞。
局限性
- 要求受害者连接到攻击者控制的 telnet 服务器。
- 在详细模式(
-v标志)下影响最大。 - Telnet 协议已被弃用且很少使用。
- 没有直接的代码执行能力。
漏洞确认与修复建议
测试输出 RCVD IAC SB (terminated by 65 65, not IAC SE) 证实了漏洞的存在。预期应找到 IAC SE(字节 255, 240),但实际上找到了 65 65(ASCII ‘A’ ‘A’)。这是因为 CURL_SB_LEN 由于无条件的 subpointer -= 2 返回了不正确的值。这是一个逻辑错误,不是内存损坏。
建议的修复方法:
|
|
应用于两处:第 1208-1211 行和第 1223-1227 行。
由于此信息泄露仅出现在详细输出中,因此不被视为安全漏洞,但它是一个 Bug。项目的最终修复方案是在错误条件下返回错误,而不是尝试继续处理(相关PR)。