CVE-2023-38545 - 我如何在CURL中造成堆溢出

admin 2023年10月13日22:28:30评论11 views字数 2855阅读9分31秒阅读模式



CVE-2023-38545 - 我如何在CURL中造成堆溢出

随着curl 8.4.0的发布,我们发布了安全公告以及CVE-2023-38545的所有详细信息。这个问题是curl长期以来发现的最严重的安全问题。我们将其设置为严重性HIGH。


虽然该咨询包含所有必要的细节。我想我应该再多说几句,为那些想了解这个缺陷如何工作以及如何发生的人扩展解释。


背景

自2002 年 8 月以来,curl 就支持SOCKS5。


SOCKS5 是一种代理协议。这是一个相当简单的协议,用于通过专用的“中间人”建立网络通信。例如,该协议通常用于建立通过 Tor 完成的通信,也用于从组织和公司内部访问互联网。


SOCKS5 有两种不同的主机名解析器模式。客户端在本地解析主机名并将目标作为解析地址传递,或者客户端将整个主机名传递给代理并让代理本身远程解析主机。


2020 年初,我给自己分配了一个长期存在的旧卷曲问题:将连接到 SOCKS5 代理的函数从阻塞调用转换为非阻塞状态机。例如,当应用程序执行大量并行传输且全部通过 SOCKS5 传输时,这一点就非常明显。


2020 年 2 月 14 日,我在 master 中完成了这一更改的主要提交。它在 7.69.0 中发布,作为具有此增强功能的第一个版本。推而广之,第一个版本也容易受到 CVE-2023-38545 的影响。


一个不太明智的决定

当有更多网络数据需要处理时,状态机会被重复调用,直到完成:即建立连接时。


在函数的顶部我做了这个:

bool socks5_resolve_local =  (proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;

此布尔变量保存有关curl 是否应解析主机或仅将名称传递给代理的信息。此分配是在顶部完成的,因此对于状态机运行时的每次调用都是如此。


状态机从 INIT 状态开始,这是今天故事时间的主要错误所在。该缺陷是从函数转变为状态机之前继承的。


if(!socks5_resolve_local && hostname_len > 255) {  ocks5_resolve_local = TRUE;}

SOCKS5 允许主机名字段最长为 255 个字节,这意味着 SOCKS5 代理无法解析更长的主机名。发现主机名太长。卷曲代码做出了错误的决定,而是切换到本地解析模式。它将用于此目的的局部变量设置为 TRUE。(这种情况是很久以前添加的代码遗留下来的。我认为像这样切换模式是完全错误的,因为用户要求远程解析curl应该坚持这一点或失败。仅仅切换甚至不可能起作用,即使在“良好”的情况下。)


然后状态机切换状态并继续。


问题触发

如果状态机由于没有更多数据可使用而无法继续,例如 SOCKS5 服务器不够快,则会返回。当有数据可继续处理时,它会再次被调用。过了一会儿。


但是现在,再次查看函数顶部的局部变量socks5_resolve_local 。它再次根据代理模式设置为一个值 -由于主机名太长而无法记住更改的值。现在它再次保存一个值,表示代理应该远程解析该名称。不过名字太长了……


curl 在内存缓冲区中构建一个协议帧,并将目标复制到该缓冲区。由于代码错误地认为应该传递主机名,因此即使主机名太长而无法容纳,内存复制也可能会溢出分配的目标缓冲区。当然取决于主机名的长度和目标缓冲区的大小。


目标缓冲区

分配的内存区域curl 用于构建协议帧以发送到代理,与常规下载缓冲区相同。它只是在传输开始之前重复用于此目的。下载缓冲区默认为 16kB,但也可以根据应用程序的请求设置为使用不同的大小。curl 工具将缓冲区大小设置为 100kB。可接受的最小大小为 1024 字节。


如果缓冲区大小设置为小于 65541 字节,则可能会发生溢出。尺寸越小,可能的溢出就越大。


主机名长度

URL 中的主机名没有实际的大小限制,但 libcurl 的 URL 解析器拒绝接受超过 65535 字节的名称。DNS 仅接受 253 字节以内的主机名。因此,长度超过 253 字节的合法名称是不常见的。超过 1024 的真实姓名几乎是闻所未闻的。


因此,恶意行为者几乎需要将超长主机名输入到该等式中才能触发此缺陷。在攻击中使用它。该名称需要比目标缓冲区长,以使内存复制覆盖堆内存。


主机名内容

URL 的主机名字段只能包含八位字节的子集。一系列字节值显然是无效的,会导致 URL 解析器拒绝它。如果 libcurl 构建为使用 IDN 库,则该库也可能会拒绝无效的主机名。因此,只有在主机名中使用正确的字节集时才会触发此错误。


攻击

控制使用客户端的 libcurl 通过 SOCKS5 代理(使用代理解析器模式)访问的 HTTPS 服务器的攻击者可以使其通过 HTTP 30x 响应将精心设计的重定向返回到应用程序。


这样的 30x 重定向将包含一个 Location: 标头,其样式为:


 Location:https://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/

…主机名长度超过 16kB 且最多 64kB


如果使用客户端的 libcurl 启用了自动重定向跟踪,并且 SOCKS5 代理“足够慢”以触发本地变量错误,它会将精心设计的主机名复制到太小的分配缓冲区和相邻的堆内存中。


然后发生堆缓冲区溢出。


修复

由于主机名太长,curl 不应将模式从远程解析切换到本地解析。它应该返回一个错误,从curl 8.4.0 开始,确实如此。


我们现在也有针对此场景的专用测试用例。


制作人员

此问题由 Jay Satiro 报告、分析和修补。


这是迄今为止支付的最大的curl错误赏金:4,660美元(根据IBB政策,加上curl项目的1,165美元)

CVE-2023-38545 - 我如何在CURL中造成堆溢出

重写吗?

是的,如果curl是用内存安全语言而不是C语言编写的,那么这一系列缺陷是不可能出现的,但是将curl移植到另一种语言并不在议程上。我确信有关此漏洞的消息将引发一系列新的问题和呼吁,我可以叹口气,翻白眼,然后再次尝试回答这个问题。


我认为朝这个方向可行且明智的唯一方法是:

  1. 允许、使用和支持以内存安全语言编写的更多依赖项

  2. 可能逐步取代部分的curl,就像引入hyper一样。

然而,这种发展目前正在以近乎冰冷的速度发生,并且清楚地表明了所涉及的挑战。在可预见的将来,curl 将继续使用 C 语言编写。


当然,欢迎每个对此不满意的人卷起袖子开始工作。


包括针对curl 8.4.0报告的最新两个CVE,累计总数表明,如果我们使用内存安全语言,在curl中发现的41%的安全漏洞可能不会发生。而且:在我们介绍前 80% 的 C 相关问题期间,Rust 语言甚至不可能用于此目的。


它在我的灵魂中燃烧

现在阅读代码不可能不看到这个错误。是的,我确实很痛苦,不得不接受这样一个事实:我在没有注意到的情况下犯了这个错误,而且这个缺陷在代码中长达 1315 天都没有被发现。我道歉。我不过是一个人。


它可以通过一组更好的测试来检测到。我们对代码反复运行了多个静态代码分析器,但没有一个发现该函数存在任何问题。


事后看来,在超过 200 亿个实例中安装的代码中传送堆溢出并不是我推荐的体验。





感谢您抽出

CVE-2023-38545 - 我如何在CURL中造成堆溢出

.

CVE-2023-38545 - 我如何在CURL中造成堆溢出

.

CVE-2023-38545 - 我如何在CURL中造成堆溢出

来阅读本文

CVE-2023-38545 - 我如何在CURL中造成堆溢出

点它,分享点赞在看都在这里


原文始发于微信公众号(Ots安全):CVE-2023-38545 - 我如何在CURL中造成堆溢出

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年10月13日22:28:30
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2023-38545 - 我如何在CURL中造成堆溢出http://cn-sec.com/archives/2109017.html

发表评论

匿名网友 填写信息