Hartwork Blog

Free Software, Music, Chinese Chess Archive Topics RSS feed GitHub profile Museum Contents © 2025 Sebastian Pipping - Powered by Nikola

递归之殇:CVE-2024-8176 背后的故事 / Expat 2.7.0 发布,包含安全修复

2025-03-13 22:38

对于不熟悉 Expat 的读者:

libexpat 是一个快速的流式 XML 解析器。与 libxml2 一起,Expat 是用 C(特别是 C99)编写的最广泛使用的自由软件 XML 解析器之一。 它是跨平台的,并以 MIT 许可证授权。

Expat 2.7.0 今天早些时候发布了。 我将使这篇文章比平常更详细,因为在许多方面,关于这个版本的可讲述的内容比一般的 libexpat 版本更多:这次有一个故事。

2.7.0 版本包含哪些内容?

现在发布版本的主要动机是将针对长期存在的漏洞的修复程序提供给用户:稍后我将详细介绍该漏洞——CVE-2024-8176。首先,这个版本还有什么?

像往常一样,还有对两个官方构建系统的修复,以及文档的改进。

Mark Brand 有一个新的 fuzzer xml_lpm_fuzzer,OSS-Fuzz 已经开始将其包含在其每日持续模糊测试中;该 fuzzer 基于 Clang 的 libFuzzer 和 Google 的 libprotobuf-mutator (LPM),它应用了一种 coverage-guided fuzzing 的变体,称为 structured fuzzing。集成该新 fuzzer 的一个辅助工作是使依赖项 libprotobuf-mutator 支持 Ubuntu 24.04、22.04 和 20.04 附带的 Protobuf 版本:我相关的上游工作 对所有人可用。

此版本的另一个有趣的副产品是(无害的)TOCTTOU 问题,该问题是通过静态分析在一个基准测试辅助工具中发现的,该工具与核心 libexpat 一起提供。 如果您没有听说过这种竞争条件漏洞,但很好奇,那么相关的 pull request 可能会引起您的兴趣:它是一个真实示例中的教科书式 TOCTTOU。

此版本中的另一件新事物是 Windows 二进制文件现在由 GitHub Actions 而不是 AppVeyor 构建,并且不仅仅是 32 位,还有 64 位。 我已经在 1 月 21 日将 64 位二进制文件发布后添加到之前的版本 Expat 2.6.4 中,但直到现在它才成为发布过程的常规部分。

漏洞报告

那么这个长期存在的漏洞是怎么回事? 在 2022 年 7 月(大约两年半前),Google Project ZeroJann HornSpectre/Meltdown 因发现 libexpat 中的一个问题,包括修复的想法,通过电子邮件与我联系。

他发现的东西可以被认为是“billion laughs”的线性版本——一个线性链(所谓的通用实体),而不是一棵树——像这样:

<!DOCTYPE doc [
 <!ENTITY g0 ''>
<!ENTITY g1 '&g0;'>
<!ENTITY g2 '&g1;'>
]>
<doc>&g2;</doc>

除了不是两个(或三个)级别,而是数千个。 为什么数千个实体引用的链对 libexpat 来说是一个问题? 因为递归,因为递归的 C 函数调用:每次调用一个函数都会增加堆栈,如果函数相互递归调用,并且攻击者控制的输入可以影响递归调用的次数,那么通过正确的输入,攻击者可以强制堆栈溢出到堆中:堆栈溢出、段错误、拒绝服务。 这取决于目标机器的堆栈大小,需要多少级别的嵌套才能命中:23,000 级别的嵌套足以在一台机器上命中,但不能在另一台机器上命中。

介绍或引导人们了解递归的教育应该附带警告; 递归不仅仅是美丽的,一种思考工具,并且通常允许更简单的解决方案——它也有黑暗的一面:一个很大的固有安全问题。 文章 The Power of 10: Rules for Developing Safety-Critical Code 在 2006 年警告了递归的使用,但 Expat 的开发早在 1997 年就开始了。

早在最初的电子邮件中,Jann 就分享了他认为的修复方法——避免(或解决)递归——并且附带了一个概念验证补丁,说明如何才能普遍做到这一点。 与其他 Project Zero 发现不同,这个问题没有 90 天期限,因为——虽然考虑了堆栈冲突 并且这是一种理论上的可能性——拒绝服务被认为是现实的影响。 应该注意的是,此风险评估不附带任何保证。

漏洞处理过程

有两件事对我来说变得显而易见:

  1. 似乎该漏洞可能有多个“面孔”或变体,并且唯一真正的修复方法实际上是从 Expat 中有效地删除_所有_剩余的递归。 这不是 C 软件(尤其是 libexpat)中第一次出现递归问题:Samanta Navarro 已经在 2022 年 2 月解决了 libexpat 代码中不同位置的易受攻击的递归。 再次感谢!
  2. 这将是一堆工作,与我在 Expat 中作为与 Expat 无关的日常工作之外的无偿志愿角色不太匹配,并且如果没有主题细节级别的合作伙伴,也不是没有风险。 我之前在 为 Expat 2.4.0 修复 billion laughs 方面所做的工作使我期望这会类似,但更大。

有了这种期望,这个问题就开始老化而没有得到解决,在某种程度上,我对这个问题感到麻木,并且长期以来一直拖延。 我经常与我的朋友、记者和安全研究员 Hanno Böck 讨论这个话题,我曾与他分享过这个问题。 他认为,即使没有修复,也应该在某个时候公开这个问题。

我反对在没有修复的情况下发布的一个原因是,很明显,在缺乏廉价的干净修复的情况下,供应商和发行版将开始应用快速破解,这将产生误报(即拒绝被错误分类为攻击的格式良好的良性 XML),使一半的问题未得到修复,并使生态系统处于下游补丁的潜在异构状态,例如,在 openSUSE 中会拒绝一个文件,但在 Debian 中可以很好地解析它——反之亦然:一场大混乱。

我最终得出结论,该漏洞不能再在我的收件箱中未修复地存在一年,需要在发布之前进行修复,以避免造成混乱,而且我必须采取行动。

与公司联系以寻求帮助

在 2024 年初,我开始考虑寻找更多帮助的方法,并在包含在 Expat 2.6.2 中的更改日志中添加了一个寻求帮助的横幅。 我开始起草一封电子邮件,我将发送给已知在硬件中使用 libexpat 的公司。 我已经开始维护一份(绝不是完整的)在硬件中使用 Expat 的公司公开列表,现在派上了用场。

在 2024 年 4 月 14 日,我开始寻找 该列表 上公司的安全联系人。 对于某些公司来说,这很容易找到,而对于另一些公司来说,我最终放弃了; 对于某些公司,我仍然不确定我是否获得了正确的地址,或者他们是否正在将我当成鸵鸟政策的一部分。 我希望更多的公司会开始提供 /.well-known/security.txt; 在 2025 年,查找漏洞报告联系人仍然是实际工作,不应该是这样。

所以然后我使用一个模板,向大约 40 家公司发送了邮件,如下所示:

Hello ${company},

this e-mail is about ${company} product IT security.
Are you the right contact for that? If not please forward
it to the responsible contact within ${company} — thank you!
On the security matter:
It has come to my attention that ${company} products and
business rely on libexpat or the "Expat" XML parser library,
e.g. product ${product} is using libexpat according to
document [1]. I am contacting you as the maintainer of
libexpat and its most active contributor for the last 8
years, as can be seen at [2]; I am reaching out to you today
to raise awareness that:
- All but the latest release of libexpat (2.6.2) have
 security issues known to the public, so every product
 using older versions of libexpat can be attacked through
 vulnerable versions of libexpat.
- Both automated fuzzing [3] and reports from security
 researchers keep uncovering vulnerabilities in libexpat,
 so it needs a process of updating the copy of libexpat
 that you bundle and ship with your products, if not
 already present.
- My time on libexpat is unfunded and limited, and there is
 no one but me to constantly work on libexpat security and
 to also progress on bigger lower priority tasks in
 libexpat.
- There is a non-public complex-to-fix security issue in
 libexpat that I have not been able to fix alone in my
 spare time for months now, that some attackers may have
 managed to find themselves and be actively exploiting
 today.
I need partners in fixing that vulnerability.
Can ${company} be a partner in fixing that vulnerability,
so that your products using libexpat will be secure to use
in the future?
I am looking forward to your reply, best

Sebastian Pipping
Maintainer of libexpat

[1] ${product_open_source_copyright_pdf_url}
[2] https://github.com/libexpat/libexpat/graphs/contributors
[3] https://en.wikipedia.org/wiki/Fuzzing

回复来了

我收到的公司回复各不相同:

在某种意义上,这很有趣也很有趣,而在另一种意义上,这并不有趣。

下一站:保密

接下来是我要求公司与我签署一个简单的自由格式的 NDA。 公司没有为此做好准备。 为什么我要索要 NDA 和 TLP:RED? (1) 确保获得详细信息的人需要合作进行真正的修复,而不仅仅是猴子修补他们自己的设置,以及 (2) 避免我之前提到的异构故障修复的情况,如果在出现真正的修复之前发生泄漏,这种情况很可能发生。

一些讨论已经在 NDA 阶段失败了,而另一些讨论幸存下来,并继续通过视频通话,我详细解释了 Jann 的发现。

值得注意的是,我知道许多漏洞奖励计划排除了整个拒绝服务类别,所以我将分享预期影响与签署 NDA 联系起来,以减少每个人丢弃它的机会“哦,'只是'拒绝服务,我们会通过”。

最终团队和安全工作

简单地说,我找到了两个主要的合作伙伴公司:Siemens 和_一家不希望被命名的公司_,让我们称他们为“未命名公司”。 西门子开始开发候选修复程序,未命名公司开始评估他们可以付费给其他公司来帮助他们的选择,这让 LinutronixRed Hat 也参与其中。

西门子承担了构建者的角色,而 Linutronix、Red Hat 和我提供了各种类型的质量保证。 虽然我们没有日夜工作,但公平地说,自 2024 年 5 月以来,我们一直在处理这个问题——大约 10 个月

漏洞的三个面孔

事实证明,该漏洞确实有多个——三个——面孔:

1. 字符数据中的通用实体

<!DOCTYPE doc [
 <!ENTITY g0 ''>
<!ENTITY g1 '&g0;'>
<!ENTITY g2 '&g1;'>
]>
<doc>&g2;</doc>

2. 属性值中的通用实体

<!DOCTYPE doc [
 <!ENTITY g0 ''>
<!ENTITY g1 '&g0;'>
<!ENTITY g2 '&g1;'>
]>
<dockey='&g2;'/>

3. 参数实体

<!DOCTYPE doc [
 <!ENTITY % p0 ''>
<!ENTITY % p1 '&#37;p0;'>
<!ENTITY % p2 '&#37;p1;'>
<!ENTITY % define_g0 "<!ENTITY g0 '&#37;p2;'>">
%define_g0;
]>
<doc/>

第三个变体“参数实体”重用了我 2013 年利用漏洞 Parameter Laughs (CVE-2021-3541) 的想法:它使用了相同的延迟解释机制。

结论和感谢

毫不夸张地说,如果没有 Berkay Eren Ürün——修复程序的主要作者——以及他在西门子的经理 Thomas Pröll 博士,今天就不会有修复程序:来自我的一个大而个人的“谢谢!”。

感谢未命名公司、Linutronix、Red Hat 帮助这架飞机飞行!

感谢 Jann Horn 的白帽研究和演示补丁,为修复指明了道路!

感谢所有为发布 Expat 做出贡献的人!

并请告诉您的朋友:

请将递归留给数学,并将其排除在(特别是 C)软件之外:它会杀死人,并且会再次杀死人。 谨代表 libexpat 致以问候,请参阅 CVE-2022-25313CVE-2024-8176 以获取证明。

有关此版本的更多详细信息,请查看更改日志

如果您维护 Expat 软件包或 Expat 的捆绑副本,或者在某处固定了 Expat 的版本,请更新到 2.7.0。 谢谢你!

Sebastian Pipping Expat Security XML Previous post Next post