本文讲述了一种新颖的供应链攻击方法,作者通过上传恶意Node.js包到npm注册表,利用内部包名称寻找目标公司的安全漏洞,成功入侵了苹果、微软等多家知名企业。主要源于开发者对代码包的盲目信任以及依赖管理工具的设计缺陷。
自从我开始学习编码以来,我就对我们在一个如此简单的命令中所投入的信任水平感到着迷:
pip install package_name
一些编程语言,比如Python,提供了一种简单的、或多或少是官方的方法来为你的项目安装依赖。这些安装程序通常与公共代码库相关联,任何人都可以自由地上传代码包供他人使用。
你可能已经听说过这些工具 — Node有npm
和npm注册表,Python的pip
使用PyPI(Python包索引),而Ruby的_gems_可在... RubyGems上找到。
在下载和使用来自这些来源的包时,你实际上在信任其发布者在你的机器上运行代码。那么,这种盲目的信任是否可以被恶意行为者利用呢?
当然可以。
没有任何包托管服务能够保证用户上传的所有代码都是无恶意软件的。过去的研究显示,typosquatting — 利用流行包名称的拼写错误版本进行的攻击 — 在获取全球随机PC的访问权限上可能极为有效。
其他知名的依赖链攻击路径包括使用各种方法妥协现有包,或者以不再存在的依赖名称上传恶意代码。
在2020年夏天与我一起尝试黑入PayPal时,Justin Gardner( @Rhynorater)分享了一段在GitHub上找到的Node.js源代码。
这段代码本是为了内部PayPal使用,其package.json
文件中似乎包含了公共和私有依赖的混合 — 来自npm的公共包,以及最有可能在PayPal内部托管的非公共包名称。当时,这些名称在公共npm注册表中并不存在。
由于不清楚决定哪个包来自哪里,提出了一些问题:
不再拖延,我开始制定一个计划来回答这些问题。
这个构想是将我自己的“恶意”Node包上传到npm注册表下所有未索取的名称,这些包会从每台安装它们的计算机“打电话回家”。如果任何包最终被安装在PayPal拥有的服务器上 — 或者任何其他地方 — 包内的代码会立即通知我。
在这一点上,我觉得有必要明确指出,在本次研究期间,所有被攻击的组织都已提供许可,允许对其安全进行测试,无论是通过公共漏洞赏金计划还是通过私下协议。请在没有授权的情况下不要进行这种测试。
幸运的是,npm允许在包安装时自动执行任意代码,使我能够轻松创建一个Node包,通过其preinstall
脚本收集有关每台安装该包的机器的一些基本信息。
权衡如何根据数据识别组织与避免收集过于敏感的信息,我决定只记录每次独特安装的用户名、主机名和当前路径。连同外部IP,这些数据刚好足以帮助安全团队根据我的报告识别可能存在漏洞的系统,同时避免我的测试被误认为是实际攻击。
现在剩下的一件事 — 我该如何将这些数据发送回给我?
考虑到大多数潜在目标可能深处于保护严密的公司网络中,我认为DNS外泄是最佳选择。
通过DNS协议将信息发送到我的服务器并不是测试本身工作的必要条件,但这样做确实能确保流量在外传时不太可能被阻止或检测到。
数据被十六进制编码并作为DNS查询的一部分发送,该查询到达我的自定义权威名称服务器,可能是直接或通过中间解析器。该服务器配置为记录每个接收到的查询,基本上记录了每台下载了这些包的机器。
在攻击的基本计划制定好后,现在是时候发现更多可能的目标了。
第一种策略是查找可以攻击的替代生态系统。因此,我将代码移植到Python和Ruby,以便能够将类似的包上传到PyPI(Python包索引)和RubyGems。
但可以说,这个测试中最重要的部分是尽可能多地找到相关的依赖名称。
花费几天的时间寻找一些目标公司私有包名称后发现,许多其他名称可以在GitHub上,以及在主要的包托管服务中找到 — 在意外发布的内部包中 — 甚至在各种互联网论坛的帖子中。
然而,找到私有包名称的最佳地点居然是在... JavaScript文件中。
显然,在内部package.json
文件中,包含JavaScript项目依赖名称的文件在构建过程中容易嵌入到公共脚本文件中,暴露内部包名称。类似地,这些文件中的泄露内部路径或require()
调用可能也会包含依赖名称。苹果、Yelp和特斯拉只是一些以这种方式暴露内部名称的公司的例子。
在2020年下半年,多亏了@streaak的帮助和他卓越的侦查技能,我们能够自动扫描数百万个目标公司的域名,并提取数百个在npm注册表尚未索取的JavaScript包名称。
然后,我在所有发现的名称下将我的代码上传到包托管服务,并等待回调。
成功率简直令人惊叹。
从开发人员在自己机器上偶然犯的错误,到配置错误的内部或基于云的构建服务器,再到系统性脆弱的开发管道,有一件事情是明确的:蹭合法的内部包名称几乎是进入一些最大科技公司网络的可靠方法,获得远程代码执行,并可能允许攻击者在构建过程中添加后门。
这种我开始称之为_依赖混淆_的漏洞,到目前为止已在超过35个组织中被发现,覆盖所有测试的三种编程语言。大部分受影响的公司人数都在1000人以上,这很可能反映了较大组织内部库使用的更高普遍性。
由于JavaScript依赖名称更容易被发现,几乎75%的所有记录回调来自npm包 — 但这并不一定意味着Python和Ruby较不易受到攻击。事实上,尽管在我的搜索中仅能识别出八个组织的内部Ruby gem名称,但其中四家公司的RubyGems通过依赖混淆,最终发现了存在的脆弱性。
其中一家公司是加拿大电商巨头Shopify,在我上传的一个名为shopify-cloud
的Ruby gem仅几小时后,他们的构建系统便自动安装了该gem,并试图运行其内部代码。Shopify团队在一天内提出了修复,并因此给予了$30,000的漏洞赏金。
此外,来自苹果的另一笔$30,000奖励,源于我在2020年8月上传至npm的Node包代码在其网络的多台机器上被执行。受影响的项目似乎与苹果的身份验证系统相关,外部称为Apple ID。
当我提到这个漏洞可能允许攻击者在Apple ID中注入后门时,苹果并不认为这种影响程度准确体现问题,并表示:
在一个运营中服务中实现后门需要更复杂的事件序列,并且是一个具有额外隐含意义的非常具体的术语。
然而,苹果确实确认,借助这种npm包技巧,可以实现远程代码执行。根据包安装的流程,问题在我报告后两周内得到修复,但漏洞赏金是在公布本文前不到一天才给予的。
在针对其他公司的几个成功攻击中,也可以观察到npm包在内部服务器和个人开发人员电脑上的安装现象,有时这些安装会在包上传后的数小时,甚至几分钟内发生。
哦,还有最开始的PayPal名称?也成功了,最终获得了又一笔$30,000的赏金。事实上,大多数获奖的漏洞赏金都设在每个程序政策允许的最高金额,有时甚至更高,这表明依赖混淆漏洞的常规高严重性。
其他受影响的公司包括Netflix、Yelp和Uber。
尽管发现了大量的依赖混淆案例,但有一个细节 — 在一定程度上现在仍然不清楚:_为什么_会发生这种情况?这种漏洞的主要根本原因是什么?
大多数受影响的组织显然不愿意分享其根本原因和缓解策略的进一步技术细节,但在我的研究和与安全团队的沟通中,确实出现了一些有趣的细节。
例如,Python依赖混淆的主要罪魁祸首似乎是一个被称为--extra-index-url
的“设计不安全”的命令行参数。因此,在使用此参数与pip install library
指定你自己的包索引时,你可能发现它按预期正常工作,但pip
在后台实际执行的操作大致如下:
library
library
因此,将一个名为library 9000.0.0
的包上传到PyPI将导致以上示例中的依赖被劫持。
尽管这一行为早已为人所知,但仅仅在GitHub上搜索--extra-index-url
就足以找到一些属于大型组织的脆弱脚本 — 其中包括影响微软.NET Core组件的一个漏洞。这个漏洞不幸的是由于不在.NET漏洞赏金项目的范围内,最终也没有允许对.NET Core添加后门。
Ruby的gem install --source
也以类似的方式工作,但我无法确认其使用是否为我发现问题的根源。
当然,将--extra-index-url
更改为--index-url
是一种快速且直接的修复,但其他一些变种的依赖混淆却证明难以缓解。
JFrog Artifactory是一款广泛用于托管各种类型内部包的软件,它提供将内部和公共库混合到同一“虚拟”库的可能性,极大简化了依赖管理。然而,多个客户表示,Artifactory使用上面描述的完全相同的脆弱算法来决定是提供具有相同名称的内部包还是外部包。在撰写时,没有办法改变这一默认行为。
报道称JFrog意识到了这个问题,但一直将其可能的修复视为“功能请求”,并且没有预估完成时间,而其一些客户则不得不在此期间对依赖管理实施系统性政策变更以缓解依赖混淆。
微软也提供了一项类似的包托管服务,名为Azure Artifacts。由于我的一份报告,该服务进行了某些小的改进,以确保能够提供可靠的解决方法来对抗依赖混淆漏洞。有趣的是,这个问题并不是通过测试Azure Artifacts本身发现的,而是通过成功攻击微软自己的基于云的Office 365而得知的,该报告得到了Azure最高$40,000的奖励。
有关根本原因和预防建议的更多深入信息,请查看微软的白皮书“ 3 Ways to Mitigate Risk When Using Private Package Feeds”。
尽管许多大科技公司已经意识到这种类型的漏洞,并已在其基础设施上修复,或正在实施缓解措施,但我仍然感觉还有更多内容值得探索。
具体来说,我认为找到新的巧妙的方法来泄露内部包名称将会暴露更多的脆弱系统,而寻找替代的编程语言和仓库进行攻击将揭示一些额外的依赖混淆漏洞的攻击面。
话虽如此,我衷心鼓励你,无论你经验如何,都花些时间去尝试一下思想中隐隐浮现的想法 — 无论这是否与依赖管理安全有关。
- 原文链接: medium.com/@alex.birsan/...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,在这里修改,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!