太长了,读不下去了,如果你只是在寻找高水平的点,请参阅本帖的“TL;DR摘要和高水平点”部分。
最近,我对npmregistry感兴趣,因为它在管理所有JavaScript和Node的包的安全性方面起着关键作用。在注册了一个帐户并创建了一个示例包之后,我开始查看各种web端点,以了解我正在处理的是哪种系统。
在浏览各种流行软件包时,我注意到注册表中有一个相当独特的东西:所有用户的电子邮件地址都是公开的。例如,请求我自己的个人资料页面会返回我的电子邮件地址:
抛开垃圾邮件问题不谈,这对于注册中心来说是一个有趣的选择,因为它公开了所有NPMPackage的维护者的电子邮件地址。例如,攻击者可以利用此功能枚举npm包的所有维护者的电子邮件,并将其用于有针对性的网络钓鱼活动。无论如何,这类网络钓鱼是众所周知的,对研究来说并不是特别感兴趣。然而,可能会有更多的新想法要考虑…
当看到更多的软件包和维护人员时,另一种模式开始融合:在开发人员拥有的域中发送电子邮件。正如许多开发者会告诉你的那样,在自己的域名上发一封电子邮件是peak nerd street的信条。我不想承认这一点,但当有人给我他们的电子邮件时,我注意到它有一个acustom域,我下意识地认为“这个人可能对计算机了解一两件事”。
当然,这些定制电子邮件不仅仅用于社交交流。它们用于开发人员使用的所有各种帐户。正如您可能已经猜到的,其中一个在线帐户很可能是npm注册表上的开发人员帐户。
这就提出了一个我不认为很多开发者认为的观点。通过注册并使用自定义域作为他们的主要电子邮件地址,他们可以简单地让该域以及他们对大多数在线帐户的完全控制。虽然并非普遍如此,但通常情况下,如果你控制了某人的电子邮件帐户,你只需启动并完成每个网站的密码重置过程,就可以访问他们的在线帐户。同样,npmregistry也不例外。如果你控制了某人的电子邮件,你可以重置npm帐户的密码并接管该帐户。
域名在很大程度上不是免费的,需要持续付费才能让它们在你的控制之下。在足够长的一段时间内,这可能会产生问题,因为这些域名通常会过期,并可供其他人注册。早在2015年,我就写过关于这一问题造成的漏洞的文章,随着时间的推移,我只看到越来越多利用这一问题进行攻击的可能性。
我很好奇,流行的npm软件包的维护者中是否有人在过期的域名上发过电子邮件。根据过去的经验,如果你有一个可能发生的事件,并且你投下了一张足够大的网,那么你很可能会得到结果。考虑到这一点,我编写了acustom脚本,以便为前1000个npm软件包及其依赖的依赖项寻找维护人员。对于每个包,我提取了开发人员电子邮件的完整列表,提取了电子邮件基本域,并检查这些域是否返回了除无错误之外的状态(RCODE)。其想法是,如果域在查询时返回非标准错误代码,它们可能会过期或被利用而配置错误。
结果符合预期:从维护人员电子邮件中提取的域中存在大量NXDOMAIN(未找到域)和其他DNS错误。这些域名中有许多是针对较小的软件包的,没有很多每周的安装,但也有一些是来自非常受欢迎的软件包。
例如,ajv Formats包是一个维护人员AdditiveMateur,其电子邮件地址为carlo[@]machina[dot]bio。该软件包相当受欢迎,在撰写本文时,npm每周安装约500万次:
此域名返回了一个NXDOMAIN错误,可以注册。为了确认实际上有可能用这个过期的域名接管ajv formatspackage,我开始注册它:
注册域名后,我将域名的DNS配置为将邮件从carlo[@]machina[dot]bio路由到我的个人电子邮件收件箱。通过对该地址的电子邮件路由,我为AdditiveeamateurnPM用户启动了密码重置过程:
这正是为什么对理论漏洞进行端到端确认如此重要的原因。引用本杰明·布鲁斯特(Benjamin Brewster)的话:“理论上,理论和实践没有区别。实际上,理论和实践是有区别的。”。
是什么原因导致该帐户以这种方式标记?是否创建了一些NPM安全检查来锁定所有过期域名上的电子邮件帐户?这将是一个令人印象深刻的纵深防守水平,但也许有一个更简单的解释。
我想看看联系支持部门是否真的是开发这些账户的真正障碍。为了做到这一点,我提交了一份支持票,有意写得尽可能基本:
我不想通过使用任何有说服力的语言或社会工程来影响考试,所以我把罚单上的信息说得很清楚。我只是注意到,我在重置帐户密码时出错,我希望它被解锁。如果支持需要进一步验证才能做到这一点,或者存在其他障碍,那么计划就是在那里完成研究。
支持人员确认,他们已重新启用重置帐户密码的功能。他们的回复似乎表明,由于之前发送电子邮件的问题,该帐户被标记,这在域名已过期的情况下是意料之中的。
对于这一限制,可能有一个更简单的解释,那就是他们之前在发送到文件中的电子邮件地址时遇到了问题,因此该帐户的电子邮件发送功能被禁用。这是有道理的,因为跳转的电子邮件可能会伤害你的电子邮件发件人,并将你的电子邮件标记为垃圾邮件。
然后,我再次尝试重置AdditiveMateuraAccount的密码。这一次,我收到了一封确认信,确认重置成功,并向帐户的电子邮件地址发送了一个重置链接:
果然,我收到了一封电子邮件,其中有一个链接,可以在我的收件箱中重置该帐户的帐户密码:
接下来的步骤与您预期的差不多,我设置了一个新密码,然后能够以AdditiveMateur帐户的身份成功登录:
在这一点上,我感到满意的是,影响已经被证明,我停止了对这个问题的测试。我没有对软件包进行任何更新,因为我认为影响开发者的风险远远超过了提供整个攻击链的好处。很明显,这是可能的,所以我相信采取这额外的步骤是不必要的(而且可能是鲁莽的)。
然而,有一件事仍然困扰着我:为什么这个软件包安装得如此频繁?这个软件包看起来当然有用,但每周安装约500万次(而且还在增加)是一个巨大的数字。这不符合逻辑,所以我做了更多的挖掘。我负责地向供应商报告了这个问题,但它被标记为信息性的,更多细节请参见披露时间表。
npm注册网站有一个很好的功能,允许您查看依赖于给定npm包的包。使用这个,我查看了安装ajv格式时作为依赖项安装的软件包:
该包是@angular devkit/core和schema utils的依赖项。当我们深入一个层次并找到这些包的依赖关系时会发生什么?
令人震惊的是,ajv formatspackage对Angular CLI和webpack都是一个可传递的依赖项!快速检查确认,当我安装Angular CLI时,我也在安装ajv formatspackage:
对于那些不太熟悉Angular的用户,Angular CLI基本上是构建和部署AngularApp所必需的(除非您使用的是第三方构建器)。
这一结果似乎清楚地证实了该漏洞的现实影响,前提是它被真正的恶意攻击者利用了。值得注意的是,还有一些其他流行的软件包也是以同样的方式被利用的(注册开发者电子邮件域),但为了保持本文的简洁,我决定不在本文中包含它们。
注意:截至本文撰写之时,webpackpackage被固定到schema utilsversion 3.1.1,该版本不包括ajv格式。因此,在Webpack更新其所依赖的schema UTILST版本之前,它不会以过渡方式安装。在所有后门npm包的情况下,这都是一个自然的要求,下面将介绍一个更微妙的视角。
这项研究围绕npmdependencies提出了许多有趣的问题,以及敏感依赖的妥协会产生多大的影响。例如,如果您有一个依赖于另一个包的包,并且该子包被泄露,那么主包也被泄露了吗?
为了介绍更有趣的依赖安全性,需要提到几个关于包在npm中如何工作的要点。特别需要注意的是:
不能“覆盖”已发布的软件包版本。例如,如果你发布了一个[受电子邮件保护的]版本,你就不能突然将一个后门软件包推到同一个版本。值得注意的是,情况并非总是如此。
只有当软件包的使用时间小于72小时且没有其他软件包依赖它时,才能取消发布该软件包版本。(所以,不,你不能聪明地删除一个版本,只为了重新发布一个后门版本。)
这就消除了最直接的途径,让我们有了一个明显的选择,那就是推出一个新的后门版本。毕竟,更新包依赖关系是包维护的一个常规部分,它在npm生态系统中定期发生。
熟悉依赖关系安全的人也会熟悉“锁定依赖关系”的第22条军规。在npm的情况下,这通常意味着有一个包锁。jsonfile,指定项目的整个依赖关系树。由于该文件位于根项目文件夹中,npm安装命令将始终按照运行npm安装生成该文件时的状态安装所有依赖项版本。流氓软件包更新不再是你的问题,因为你没有更新!问题解决了,对吗?
然而,这留下了另一个可能更严重的问题:易受攻击的库不再被更新。如果你锁定了所有的依赖项(以及它们的依赖项),那么你永远不会收到bug修复和关键补丁。在log4jvulnerability之后,这显然不是一条理想的路线。就像大多数事情一样,这是一个风险权衡的决定。你权衡每种方法的利弊,并据此进行。
如果攻击者故意将其受损软件包标记为“易受攻击”,该怎么办?这会把依赖的包留在哪里?依赖攻击者软件包的软件包的作者现在必须对我们之前提到的第22条军规做出决定。如果他们不更新他们的依赖关系,他们就有可能让所有使用他们软件包的人处于“易受攻击”的风险。然而,如果他们更新依赖关系,他们就有可能会有大量新代码被合并到他们的包中。
这就引出了另一个问题,难道维护人员和开发人员不能在包含更新的版本之前,检查每个依赖项中的所有新代码吗?
对这一观点最明显的反驳可能是不言而喻的:这是一种工作。我们讨论的是关于依赖者自身的差异,他们的依赖性,等等。无论严格程度如何,这样做都是一项耗费大量时间的工作。绝大多数全职软件开发人员并没有审查他们需求的每一个变化,更不用说在自己的私人时间维护软件包的志愿者了。至少可以说,要求这些志愿者完成这项艰巨的任务似乎是不现实的。
当您考虑到在包中指定版本范围的能力时,问题只会变得更加模糊。json。如果您的依赖项允许其依赖项具有灵活性(例如:>;=package_name),那么开发人员可以稍后将更新发布到新版本,并将其包含在新安装中。这意味着您可以查看所有依赖项,发现任何恶意内容,然后在以后被无缝地后门攻击。
对于那些对自己做一些学习和研究感兴趣的人来说,不妨尝试一下更新规则中依赖项的过程。在我看来,谷歌实际上在这方面做得相当好(从各方面考虑)。下面是一个示例拉动请求,让您开始:https://github.com/angular/angular/pull/45013
如果不是Angular,请选择在JavaScriptdevelopment中使用的任何流行包。当你研究这个过程时,问问自己以下几个问题:
“它是否阻止指定版本范围而不是特定单一版本的依赖关系?”
这不是测验,也没有答案或简单的答案,这意味着让你认真思考独立安全面临的问题。现实世界充满了细微差别和难题,软件开发也不例外!
显然,依赖关系安全是一个复杂的问题,所以让我们关注更简单的问题:帐户电子邮件中的过期域。在这种情况下,需要考虑以下几点:
自定义域是开发人员文化中常见的一部分,它们不太可能消失。当这些域过期时,它们确实存在被利用的风险,开发人员在将它们用于重要帐户时应该认真考虑这个问题。
在这项研究中,npm账户并不是唯一一个在注册过期域名时突然被劫持的账户。Github、Slack等各种重要网站上的账户也处于危险之中。任何使用电子邮件密码重置作为帐户访问单一因素的网站都存在此问题。
网站可能希望在其电子邮件域过期或无法路由时主动禁用帐户。他们还可能需要有一个替代路径来重置帐户的密码,以在发生这种情况时强烈验证此人是帐户所有者,并且原始帐户所有者需要再次访问他们的帐户。
在npm的案例中,我通过HackerOne向他们(Github安全团队)报告了问题,他们需要这些漏洞报告。他们将该报告称为“信息性”报告,并指出“这是我们一直在内部跟踪的事情,我们已经采取了相应的缓解措施”。他们是否实施了任何新的改变尚不清楚。有关更多信息,请参阅下面的披露时间表,或本哈克龙报告档案。我可能会建议npm用户继续假设这仍然是一个可以利用的问题。
高度安装的npm软件包的开发人员将其npm帐户注册到过期域的电子邮件地址。
注册这些过期的域名可以接管重要的npm软件包,比如ajv格式,目前每周约有500万次安装。
ajv格式包依赖于angular的核心实用程序库@angular devkit/core@angular devkit/core是@angular/cli的依赖项,后者是用于编译angular应用程序的工具。
使用自定义域的电子邮件地址的开发者应该认真考虑他们使用在线帐户的电子邮件所承担的风险。如果这个域名过期或被劫持,他们会去哪里?
目前尚不清楚npm是否因我向他们提交的报告而有所改变,npm用户可能会认为这个问题在未来仍有可能被利用。
2022年1月10日:联系machina的所有者。bio domain通过LinkedIn披露了这一问题。
2022年1月15日:意识到我错过了LinkedIn回复开发者的电子邮件。已将披露的详细信息发送到提供的电子邮件地址。
2022年1月22日:跟进npm账户和machina的凭证。完成移交的生物域转移代码。
2022年1月17日:由于npm的网站要求通过此途径提交漏洞,因此向Github的漏洞奖励计划提交了HackerOne漏洞。报告编号#1452186。
2022年1月25日:GITHUB安全团队关闭bug作为无效的“因为攻击向量包括提交一个支持请求来重新启用禁用帐户的密码重置,这被认为是社会工程,因此不符合B赏金计划下的奖励。”他们还认为这一问题有所缓解。”这是我们一直在内部跟踪的事情,我们已经采取了相应的缓解措施。"
2022年1月25日:请求公开披露,因为该漏洞明显得到了缓解,而且该报告因信息丰富而关闭。
2022年2月2日:Github拒绝披露请求,因为bug被关闭为信息性的。然而,他们表示,发表一篇评论是完全可以的。你可以在这里看到完整的HackerOne报告:https://imgur.com/a/7TQs3vx
特别感谢Michael Xu(@michaelxproxy)就这个话题向我提供咨询。他的反馈对于这些关于包裹固定的细微差别的讨论至关重要。