游客发表
2023年10月,curl作者Daniel Stenberg在邮件列表里扔出一颗炸弹:libcurl的SOCKS5代理握手环节存在堆溢出,CVE-2023-38545。CVSS评分8.8,影响从7.69.0到8.3.0的所有版本。攻击者只需要诱导你用一个恶意SOCKS5代理,就能在目标机器上执行任意代码。那天晚上我正用libcurl写一个内网穿透工具,看到消息后立刻翻了一遍自己的CMakeLists.txt——果然,依赖里锁的是8.2.1。
这不是libcurl第一次出事了。过去五年,这个库拿了至少三十个CVE。但这次不一样。堆溢出发生在socks5_resolve_local()函数里,当主机名长度超过255字节时,socks5_buffer的分配逻辑会算错大小。你传一个超长域名进去,比如一串精心构造的A记录,缓冲区就炸了。更麻烦的是,这个漏洞在客户端侧触发,服务端根本不需要配合。你连上一个恶意代理,游戏就结束了。
我翻了下curl的git log。修复补丁其实很小,就改了一行:把if(hostname_len > 255)的判断从异步解析分支挪到了同步分支前面。但这一行背后是十七年的代码演进。SOCKS5支持从2006年就进了libcurl,那会儿还是用malloc加strcpy的写法。后来重构了三次,每次都觉得“这次应该没问题了”。结果呢?一个长度检查的遗漏,把整个生态拖下水。
谁在用libcurl?说出来吓人。你的PHP、你的Node.js、你的Git、你的Docker、你的Android系统更新、你的特斯拉车机。Debian的apt、RedHat的yum、macOS的Homebrew,底层全是它。一个堆溢出,等于给全球几十亿台设备开了后门。我认识的一个运维老哥,他们公司有三千台服务器跑着CentOS 7,自带的libcurl版本是7.29.0。这个版本太老,反而不受CVE-2023-38545影响——因为漏洞代码是7.69.0才引入的。他苦笑着说:“头一回觉得版本旧是好事。”
但别高兴太早。libcurl的维护者只有Daniel一个人全职在做,外加几个志愿者。2023年他们处理了超过200个issue,合并了上千个PR。这种人力配置下,代码审计的深度必然有限。我读过socks5.c的源码,里面有不少if (len > sizeof(buf))这样的防御,但偏偏漏了hostname_len在异步路径下的边界。为什么?因为异步解析用了curl_multi的状态机,控制流跳来跳去,静态分析工具很难覆盖所有分支。
修复方案很简单:升级到8.4.0。但现实是,大量嵌入式设备、旧版Linux发行版、闭源商业软件根本不会跟进。我见过一个工业网关,固件里静态链接了libcurl 7.74,厂商说“产品生命周期内不更新”。这种设备一旦暴露在公网,就是活靶子。更讽刺的是,很多安全扫描器只检查HTTP响应头里的Server: curl/8.2.1,而libcurl作为客户端库根本不发这个头。你扫不到,不代表它不在。
我的建议?如果你写C/C++,别直接调libcurl的SOCKS5接口,用CURLOPT_PROXY加CURLOPT_PROXYTYPE时手动校验hostname长度。如果你用Python的pycurl或Node的node-libcurl,赶紧查依赖树。最稳妥的办法是:把libcurl版本钉死在8.4.0以上,然后祈祷下一个漏洞来得晚一点。毕竟Daniel已经53岁了,他不可能永远一个人扛着这个互联网的承重墙。
友情链接