引言
在软件开发的世界里,我们经常想当然地认为我们日常依赖的工具和平台是安全和可靠的。我们假设我们下载的包和我们使用的注册表是安全和值得信赖的。然而,在Lupin & Holmes,我们最近发现了npm注册表上的一个缓存投毒攻击,这是JavaScript最大的包注册表之一,可能暴露了我们的软件供应链的脆弱性以及潜在的广泛破坏。
npm注册表是JavaScript生态系统的关键组成部分,作为一个中心仓库为超过210万个包 提供服务,全球有超过1700万开发者依赖它。它已经成为一个不可或缺的资源,使他们能够轻松地分享、重用和管理项目中的依赖。每天有数百万次下载,npm注册表是无数应用程序和网站的支柱。
在本文中,我们将讨论npm上的缓存投毒攻击的细节,并探索其对更广泛软件生态系统的潜在影响。通过公开披露这个漏洞,我们旨在展示我们软件供应链中的安全性和可用性的重要性。
缓存投毒攻击:利用漏洞
这个故事实际上非常有趣。当我们为Depi 开发一个研究子模块,旨在测试Artifactories上的缓存投毒时,我们惊讶地发现世界上最著名的注册表是脆弱的:npmjs.com。
作为Depi开发的一部分,我们经常在研究模式下测试几个子模块。本质上,我们探索可能破坏我们客户软件供应链的理论攻击向量,并开发一个模块来在更大规模上进行测试和验证。一旦结果被分类,我们就专注于所有通用和奇怪的结果,直到我们有一个值得在Depi中实现的攻击向量。任何可能影响我们客户的完整性、保密性以及可用性的事情都值得测试。
我们的理论很简单:如果客户使用的Artifactories可能容易受到Cache Poisoning Denial of Service (CPDoS)条件的攻击会怎样?
CPDoS是一种网络攻击技术,由PortSwigger 在这项研究中描述。它涉及利用web缓存系统中的漏洞向用户传递恶意内容。攻击通过操纵web缓存存储和提供内容的方式工作,有效地用有害数据污染缓存。当用户请求受影响的资源时,就会提供被污染的内容而不是合法内容,可能导致服务拒绝或其他恶意结果。这种技术利用了缓存机制及其与各种web协议和头的交互中的微妙缺陷。
考虑到这一点,我们认为如果攻击者能够污染一个页面,将.tar.gz
文件的内容重定向到恶意的,或者完全拒绝该包,那将是有趣的。我们开发了一个执行此操作的模块,实现到Depi中,我们在第一次运行中就匹配到了:
registry.npmjs.org是脆弱的。
显然,我们认为这是一个误报,并且漏洞实际上并没有在第一次运行中触发,但我们后来设法确认我们确实在npmjs注册表上有了一个完整的CPDoS!
攻击依赖于一系列不同的头,这些头被发现会触发后端系统的一个错误。通过发送一个包含这些头的特殊构造的请求,攻击者可以操纵注册表的缓存系统,为一个目标包存储一个“未找到”的响应。
注意:我们不提供将触发此问题的确切的头序列,因为在撰写本文时它仍然可以被利用
以下是一个恶意请求的示例,我们设置了一个缓存破坏参数以避免对真实用户进行DoS攻击:
GET /safe-regex/-/safe-regex-1.1.0.tgz?lupin_E7A812DE-E09A-4906-A9E3-530E54AAEB41=cpdos_test HTTP/1.1
Host: registry.npmjs.org
User-Agent: lupin_test_cpdos
lupin: E7A812DE-E09A-4906-A9E3-530E54
通过随后的请求,没有头和相同的缓存键,攻击者随后可以检索缓存的“未找到”响应,有效地使包对其他用户不可访问。
GET /safe-regex/-/safe-regex-1.1.0.tgz?lupin_E7A812DE-E09A-4906-A9E3-530E54AAEB41=cpdos_test HTTP/1.1
Host: registry.npmjs.org
User-Agent: lupin_test_cpdos
第二个请求将导致以下响应:
HTTP/1.1 404 Not Found
Date: Tue, 21 May 2024 08:40:06 GMT
Content-Type: application/json
Content-Length: 21
Connection: keep-alive
CF-Ray: 8873427f1e301222-MRS
Access-Control-Allow-Origin: *
Vary: Accept-Encoding
Server: cloudflare
{
"error": "Not found"
}
我们发现缓存的响应是暂时的(几分钟),需要攻击者不时地连续发送请求以维持CPDoS。我们还观察到注册表使用了多个后端服务器,需要重复攻击以在不同的服务器上污染缓存。
虽然这次攻击是直接从Burp Suite测试的,我们已经创建了一个虚拟包来测试我们是否能够从npm install命令中污染。我们代理了npm cli来看看发生了什么,然后从当前系统中清除了缓存。在运行了几秒钟的攻击循环后,我们设法确认我们确实可以在生产环境中污染一个包:
红色:在运行攻击之前
绿色:在运行攻击时
缓存投毒攻击的影响是巨大的。攻击者可以针对流行的包,例如每周下载量超过3000万的“express”,并有效地使它们对开发者不可用。这可能导致软件开发流水线的广泛中断,导致构建失败和应用程序崩溃。
我们还使用各种IP和客户端进行了彻底的测试,确认污染不仅影响了攻击者,还影响了尝试访问目标包的其他用户。这突出了如果漏洞被恶意利用,可能会产生广泛影响。
通过成功利用这个漏洞,攻击者可以在npm注册表上造成服务拒绝,使其对用户不可用,破坏全球无数组织软件开发流程。如果针对广泛使用的包,攻击可能会特别具有破坏性,因为它可能会在软件供应链中引起连锁反应。
然而,我们也发现被污染的后端服务器存在不一致性,如果缓存已经被注册,攻击者无法使用PURGE命令重新污染它。我们还发现,在深入研究这种行为时,攻击者可能在流行包的新版本注册之前开始污染,以便在版本存在或包的缓存更新时污染安装。
软件供应链的连锁反应:后果
对npm注册表的缓存投毒攻击有着深远的后果,这些后果超出了单一包的即时可用性。当像npm注册表这样的关键组件受到破坏时,它可能会对整个软件供应链产生多米诺骨牌效应。
考虑这样一个场景,一个被广泛使用的包,如express 被攻击者针对。在其最新版本上,每周下载量超过1200万,这个包是无数web应用程序的基本构建块。
如果由于缓存投毒攻击而使包变得不可用,它可能会对所有依赖它的项目和服务产生连锁影响。
连锁反应可以在多个组织中感受到。
Github对这一漏洞的回应
当Github收到有关这个漏洞的Bug Bounty报告时,npm的所有者进行了调查。在评估了发现后,Github确定这个问题并没有构成重大的安全风险,并以信息性报告关闭了报告,同时没有修复这个问题。
Github承认了缓存投毒攻击的复杂性和有限的可靠性。他们还指出,攻击依赖于使用一个有问题的头,这已经是他们团队已知的。此外,Github强调,执行攻击需要DDoS或体积攻击(我们将讨论这个问题),这属于滥用范畴,不在他们的bug bounty计划范围内。尽管报告根据他们的声明不符合全额奖励的条件,但Github还是认可了我们的努力,并提供了500美元的小额奖励作为感谢。
现在我们对他们的以下声明感到困扰:
为了进一步执行你在最新的后续评论中描述的攻击,需要DDoS或体积攻击,这是滥用问题。我们严肃对待滥用和垃圾邮件,并有一个专门的团队追踪垃圾用户。请注意,关于体积DoS攻击的提交不在我们bounty计划的范围内。
但正如我们在报告和本文中所示,我们仅用一台机器就成功地破坏了一个生产包。攻击的可靠性只受两个问题的影响:
- 我们无法预测哪个后端将受到影响
- 我们必须每隔几分钟发送一个请求以维持污染
基于我们的测试,四分之一的请求被污染,这已经是一个大问题。我们只是提到如果攻击者发送更多的请求,攻击可能会更可靠。但为此所需的资源绝不会接近DDoS。
在撰写本文时,这个漏洞仍然可以在npmjs注册表上被利用。然而,我们将文章的初稿发送给了Github的安全团队,以获得对技术部分的总体反馈。在收到草稿后,Github的团队向我们发送了以下评论:
再次感谢您与我们分享您打算发表的博客文章的草稿,并提交了这份报告,为我们提供了加强DoS保护的机会。正如我们在这个问题的历史中讨论的那样,可重复性一直很难,需要DDoS或体积攻击才能成功利用。为了继续提高npm和我们产品的安全性,我们计划在下周一发布一个修复程序来解决这个问题,尽管它很复杂。我们再次感谢您对我们Bug Bounty计划的贡献,并期待未来的合作。
有必要声明,我们对Github的安全团队没有任何反对意见。实际上我们见过他们,他们是很棒的人,非常有趣。话虽如此,我们仍然不同意对情况的整体评估。我们坚信,这种CPDoS应该被像处于软件供应链中心的公司更认真地对待。声称所描述的攻击是体积性的,并且属于滥用类别,在我们谦虚看来是牵强的。
我们相信,在未来,这种对软件供应链的可用性攻击将被更认真地对待。越来越多的研究人员和bug猎人正在揭示CPDoS问题及其对我们生态系统的影响。
我们强烈希望安全社区所做的所有这些工作能成为软件供应链生态系统的真正警钟。
结论
Lupin & Holmes的安全研究团队发现的npm注册表上的缓存投毒攻击,让我们思考了我们软件供应链的脆弱性问题。
这种漏洞的潜在影响不容小觑。一个被破坏的注册表可能会产生巨大的后果,影响开发者、组织和最终用户。一个单一包变得不可用的连锁反应可能会打乱开发流水线,导致应用程序停机,并导致财务损失。
然而,这个漏洞显示了一个成长和改进的机会。它突显了像GitHub和npm这样的组织需要不断评估和加强他们的安全实践的必要性。
此外,缓存投毒攻击强调了在软件供应链中协作和共同责任的重要性。开发者、注册表提供商和更广泛的社区必须共同努力,确保我们依赖的包的安全性、完整性和可用性。
时间线
- 2023年3月3日,UTC 22:14 Lupin报告了漏洞
- 2023年3月6日,UTC 17:11 Github确认了报告
- 2023年3月9日,UTC 19:48 Github将问题关闭为内部重复
- 2023年5月4日,UTC 21:42 Github重新打开报告
- 2023年6月6日,UTC 18:49 Github对报告进行分类
- 2023年8月30日,UTC 22:24 Github将报告关闭为信息性并支付500$
- 2024年5月15日,UTC 09:44 Lupin开始了协调披露流程
- 2024年5月16日,UTC 21:22 Github同意披露问题
- 2024年5月24日,UTC 14:21 第一稿发送给Github
- 2024年6月1日,UTC 02:15 Github确认问题将被修复