英文原文:https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
自从我开始学习编程以来,我一直感到好奇的是,我们居然对我们放在命令行工具中这样一个简单命令如此信任:
某些编程语言(例如 Python)带有一种简单的或多或少正式的方法来为项目安装依赖项。 这些安装程序通常与公共代码存储库绑定,并且任何人都可以自由上传代码包供他人使用。
您可能已经听说过这些工具 ——Node 具有 npm 和 npm 注册表,Python 的 pip 使用 PyPI(Python 包索引),并且 Ruby’s Gems 可以在 RubyGems 上找到。
从这些来源中的任何一个下载和使用软件包时,您实际上是在相信其发布者在您的计算机上运行代码。 那么这种盲目的信任会被恶意行为者利用吗?
当然可以。
任何程序包托管服务都无法保证其用户上传的所有代码均不含恶意软件。 过去的研究表明,域名抢注(利用流行包名称的错字版本进行的攻击)在访问全球随机 PC 方面非常有效。
其他众所周知的依赖项链攻击路径包括使用各种方法来破坏现有软件包,或以不再存在的依赖项名称上传恶意代码。
在 2020 年夏季,Justin Gardner(@Rhynorater)和我一起尝试入侵 PayPal 时,他分享了在 GitHub 上发现的一些有趣的 Node.js 源代码。
该代码旨在供内部 PayPal 使用,并且在其 package.json 文件中似乎包含公共和私有依赖项的混合 —— 来自 npm 的公共程序包以及非公共程序包名称,很可能由 PayPal 内部托管。 这些名称当时在公共 npm 注册表中不存在。
由于逻辑上不清楚哪个包将从何处获得,出现了一些问题:
事不宜迟,我开始制定计划来解答这些问题。
这个想法是将我自己的 “恶意” Node 程序包以假名上传到 npm 注册表中,这将从安装它们的每台计算机上 “打电话回家”。 如果最终将任何软件包安装在 PayPal 拥有的服务器上(或其他任何地方),则会有其中的代码立即通知我。
在这一点上,我觉得很重要的一点是,必须明确指出,在此研究过程中所针对的每个组织都已允许通过公共漏洞奖励计划或通过私人协议来对其安全性进行测试。 未经授权,请勿尝试这种测试。
幸运的是,npm 允许在安装软件包时自动执行任意代码,这使我可以轻松创建一个 Node 软件包,该软件包通过其预安装脚本收集有关所安装的每台计算机的一些基本信息。
为了在基于数据识别组织的能力与避免收集太多敏感信息之间取得平衡,我决定只记录用户名,主机名和每个唯一安装的当前路径。 与外部 IP 一起使用的数据就足够了,可以帮助安全团队根据我的报告确定可能受到攻击的系统,同时避免将我的测试误认为是实际的攻击。
现在剩下一件事 —— 如何将这些数据回传?
基于大多数可能的目标主机都会位于公司内网,且受到良好的保护,我认为 DNS 渗透是解决之道。
通过 DNS 协议将信息发送到我的服务器对于测试本身的工作来说并不是必要的,但是它确实能确保流量在流出时不太可能被阻塞或检测到。
数据经过十六进制编码,并用作 DNS 查询的一部分,该 DNS 查询直接或通过中间解析器到达了我的自定义权威名称服务器。 服务器配置为记录每个接收到的查询,实质上记录了下载软件包的每台计算机的记录。
有了攻击的基本计划,现在是时候去寻找更多可能的目标了。
第一个策略是寻找可攻击的生态系统进行攻击。 因此,我将代码移植到了 Python 和 Ruby 上,以便能够分别将类似的软件包上传到 PyPI(Python 软件包索引)和 RubyGems。
但是,该测试最重要的部分是找到尽可能多的相关依赖项名称。
在搜索一些目标公司的私有软件包名称的整整几天中,发现在 GitHub 以及主要的软件包托管服务(在偶然发布的内部软件包中),甚至在各种互联网论坛的帖子中,都可以找到许多其他名称。
但是,到目前为止,找到私有程序包名称的最佳位置竟然是… 在 javascript 文件中。
显然,内部 package.json 文件(包含 javascript 项目的依赖项的名称)在构建过程中会嵌入到公共脚本文件中,从而暴露内部程序包名称,这是很常见的。 同样,这些文件中泄漏的内部路径或 require()调用也可能包含依赖项名称。 苹果,Yelp 和特斯拉只是以这种方式公开内部名称的公司的一些例子。
在 2020 年下半年,由于 @streaak 出色的侦查技能的帮助下,我们能够自动扫描目标公司的数百万个域,并提取出数百个尚未在 npm 注册表中声明的其他的 javascript 包名。
然后,我将我的代码上传到所有找到的名称下的包托管服务,并等待回调。
…
成功率简直惊人。
从开发人员在自己的机器上犯下的一次性错误,到配置错误的内部或基于云的构建服务器,再到系统性易受攻击的开发管道,有一点是很清楚的:占用有效的内部包名几乎是一种可靠的方法,可以进入一些最大的科技公司的网络,获得远程服务代码执行,并可能允许攻击者在构建过程中添加后门。
这种类型的漏洞,我已经开始称之为依赖性混淆,是迄今为止在超过 35 个组织中,在所有三种测试的编程语言中检测到的。绝大多数受影响的公司都属于 1000 多名员工类别,这很可能反映出大型组织内部 library 使用率较高。
由于 javascript 依赖名更容易找到,几乎 75% 的日志回调来自 npm 包,但这并不一定意味着 Python 和 Ruby 不太容易受到攻击。事实上,尽管在我的搜索过程中只能识别出属于 8 个组织的内部 Ruby gem 名称,但这些公司中有 4 个很容易被 RubyGems 混淆。
加拿大电子商务巨头 Shopify 正是这样一家公司,它的编译系统在我上传一个名为 shopifycloud 的 Ruby gem 后的几个小时就自动安装了它,然后试图在里面运行代码。Shopify 团队在一天内就准备好了修复程序,我因此而获得了 30000 美元的漏洞奖励。
另一笔 3 万美元的奖励来自苹果,此前我在 2020 年 8 月上传到 npm 的一个节点包中的代码在其网络内的多台机器上执行。受影响的项目似乎与苹果的认证系统有关,外界称之为苹果 ID。
当我提出这个 bug 可能允许一个威胁参与者在 Apple ID 中注入后门的想法时,Apple 并不认为这个级别的影响准确地代表了这个问题,并声明:
在操作服务中实现后门需要一个更复杂的事件序列,并且是一个带有额外含义的非常具体的术语。
不过,苹果确实证实,通过使用这种 npm 包技术,可以在苹果服务器上实现远程代码执行。根据软件包安装的流程,这个问题在我的报告发布后两周内就解决了,但是漏洞奖励在发布这篇文章前的不到一天内就获得了。
在其他几次针对其他公司的成功攻击中,可以观察到内部服务器和开发人员的 PC 上都安装了相同主题的 npm 包,其中有些安装通常在软件包上传数小时甚至数分钟后进行。
哦,这一切的起源 PayPal 也在其中?这些措施也奏效了,结果又得到了 3 万美元的赏金。事实上,大多数奖励的漏洞奖金都被设置为每个程序策略允许的最大金额,有时甚至更高,这证实了依赖性混乱 bug 的严重性通常很高。
其他受影响的公司包括 Netflix、Yelp 和 Uber。
尽管有大量的依赖性混淆发现,但有一个细节在某种程度上仍然不清楚:为什么会发生这种情况?这种脆弱性背后的主要根源是什么?
可以理解的是,大多数受影响的组织不愿意进一步分享有关其根本原因和缓解策略的技术细节,但在我的研究期间以及我与安全团队的沟通中,确实出现了一些有趣的细节。
例如,Python 依赖关系混乱的主要原因似乎是错误地使用了名为–extra index url 的 “不安全的 by design” 命令行参数。将此参数与 pip install library 一起使用以指定您自己的包索引时,您可能会发现它按预期工作,但 pip 在幕后实际执行的操作如下所示:
因此,将名为 library 9000.0.0 的包上载到 PyPI 将导致上述示例中的依赖关系被劫持。
尽管这种行为已经广为人知,但只要在 GitHub 中搜索 —— 额外的索引 url 就足以找到一些属于大型组织的易受攻击的脚本 —— 包括影响 Microsoft.NET 核心组件的 bug。该漏洞可能允许向.NET Core 添加后门,但不幸的是,在.NET bug bounty 程序中发现该漏洞超出了范围。
Ruby 的 gem install-source 也以类似的方式工作,但我无法确认它的使用是否是我发现的这一切问题的根本原因。
当然,将–extra index url 改为–index url 是一个快速而直接的解决方案,但是其他一些依赖关系混乱的变体被证明更难缓解。
JFrog Artifactory 是一种广泛使用的用于托管所有类型的内部包的软件,它提供了将内部库和公共库混合到同一个 “虚拟” 存储库中的可能性,极大地简化了依赖关系管理。然而,多个客户表示,Artifactory 使用与上面描述的完全相同的易受攻击的算法来决定使用相同名称服务内部和外部包。在编写本文时,无法更改此默认行为。
据报道,JFrog 已经意识到了这个问题,但一直将其可能的修复视为一个 “特性请求”,看不到 ETA,而它的一些客户则求助于对依赖关系管理应用系统性的策略更改,以减轻其间的依赖关系混乱。
微软还提供了一个类似的包托管服务,名为 Azure Artifacts。作为我的报告结果之一,对该服务进行了一些小的改进,以确保它能够为依赖性混淆漏洞提供可靠的解决方法。有趣的是,这个问题并不是通过测试 Azure Artifacts 本身发现的,而是通过成功攻击微软自己的基于云的 office365 而发现的,该报告使得我在 Azure 获得了最高奖励 4 万美元。
有关根本原因和预防建议的更深入信息,您可以查看 Microsoft 的白皮书 “使用私有包源时降低风险的 3 种方法”。
虽然许多大型科技公司已经意识到了这种漏洞,并且已经在其基础设施中修复了它,或者正在努力实施缓解措施,但我仍然感觉到还有更多需要去挖掘的地方。
具体地说,我相信找到新的、聪明的方法来泄漏内部包名会更易受攻击的系统暴露出来,若是寻找替代编程语言和存储库又将暴露出一些额外的漏洞。
话虽如此,我衷心地鼓励您,无论您的经验水平如何,花点时间在脑海中尝试一下这个想法 ——— 不管它是否与依赖关系管理安全相关。