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()读取越界内存
内存布局(struct 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协议已弃用且很少使用
- 无直接代码执行能力
漏洞确认与讨论
测试输出作为证明
测试输出中显示的内容证实了漏洞的存在:
|
|
这是漏洞的表现。代码期望在终止符位置找到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→ 回到NCURL_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’。
建议修复
|
|
应用于两个位置:第1208-1211行和第1223-1227行。
影响评估
同意严重性为低:
- 需要连接到恶意telnet服务器
- 主要在详细模式下可见
- Telnet已弃用
- 无直接代码执行能力
但这是一个有效的错误,导致:
- 不正确的长度计算
- 错误的终止符检测(由测试证明)
- 子选项处理中的潜在逻辑错误
项目方响应与修复
curl团队确认了该问题但将其归类为"信息性"而非安全漏洞,因为信息泄露仅发生在详细输出中。他们提出了修复方案,即为此错误条件返回错误而非尝试继续处理,并提交了相关PR(https://github.com/curl/curl/pull/20108)。
披露
根据项目透明政策,该报告已公开披露。