大受欢迎且广泛部署的几百个 Java 库和 JVM 编译器,它们下载依赖项时,仍在用 HTTP 协议,却没有校验完整性。本来只是想给一个小型项目做一个简单漏洞报告,不料猛地发现了这个安全漏洞,影响了基于 Java 虚拟机(JVM)做开发的整个生态系统。
初探
Max Veytsman于 2014 年发表了一篇很棒的文章,名为《如何接管任意Java(或Closure或Scala)开发人员的计算机》,这项工作正是基于这篇文章开展的。
当时,由 Sonatype 运行的 Maven 中央仓库在提供 JAR 文件时,还不支持 SSL(HTTPS)。多亏 Max 的文章,Sonatype 几天内就修复了这个问题。我强烈建议你在阅读本文之前,至少先略读一下那篇文章。虽然它已经发表了 5 年,但其中的警告仍然有效,不仅如此,现在还能适用于 Kotlin 和 Groovy 的开发人员。
然而,这一次不是因为存储库主机缺乏对 HTTPS 的支持,而是因为普遍都在采用“一字之差”的 HTTP,所以直到今天,仍有成千上万的开源项目易受攻击。
Max 在他的文章中介绍了一个名为 Dilettante 的工具,“它是一个中间人代理,拦截来自【任何工件(artifact)仓库】的 JAR 包,并将恶意代码注入其中。通过 Dilettante 代理 HTTP 流量,将会在【从工件仓库下载的】任何 JAR 包里开后门。”
Dilettante 是一个简单的 POC,它所做的仅仅是让 Java 在屏幕上展现猫图片。但就是这种非常简单的技术,却可以用来大面积恶意破坏 Java 生态系统——只要项目是通过 HTTP 而不是 HTTPS 下载依赖项的。
图中猫咪说:听说你提供 JAR 包不用 ssl?
HTTPS 不仅能加密客户端和服务器之间的流量,还能保证客户端是在与其所请求的服务器进行通信,避免有人在中间冒名顶替(即 MITM,中间人攻击)。
我是怎么发现的?
有一次,我发现自己的项目中有一个工件用的是 HTTP。
这是怎么来的?
这是从Ktor仓库复制和粘贴来的。我深入研究 Ktor 仓库的历史,发现直到最近,Ktor还在用HTTP来解析依赖关系。有一点要注意,Ktor 是 JetBrains 的一个官方库。就连 JetBrains 的官方库都这样?我十分好奇,开始寻找其他哪里还有这个库。
谁容易遭受攻击?
长话短说:GitHub 上一些最流行的基于 JVM 的项目,它们曾经或仍然容易受到攻击。
由Sheetsu.com提供
注意:“已完成”列写着“是”的项目已完全解决了问题,并且已审核过,或为其之前的版本发布了 CVE 编号。
下面是上表的 Google Sheets 地址,有兴趣可以去看看。
除了上面列出的项目之外,还有一些大型社区和组织也会受此漏洞影响。
“我的世界(Minecraft)”游戏模组
这是我找的第一个地方,不出所料,马上就找到了这个漏洞,在几乎所有模组的构建的基础架构中都有它。
JetBrains
考虑到 Ktor 事件可能不是一次性事件,我开始关注 JetBrains 在 GitHub 上的项目。
Kotlin 编译器
Kotlin 是 JetBrains 开发的编程语言,可编译为 JVM、LLVM 和 JavaScript,在 Android 开发人员中很受欢迎。谷歌最近指定Kotlin现在成为Android开发的首选语言。
我在 Kotlin 编译器代码库中,发现了许多实例,它们的构建的基础设施和测试都通过 HTTP 下载依赖项.
不仅 Kotlin 编译器的源代码的依赖容易受到攻击,而且易受攻击的代码仓库也用在了 Gradle 的“buildscript”类路径,这增加了把牵连到的工件发布出去的潜在风险。
如果这还不够糟糕,那么 buildscript 类路径也用于解析以前版本的 Kotlin 编译器,从而使编译器可能遭受“Trusting Trust”攻击(参见下面更详细的内容)。
IntelliJ IDEA
IntelliJ 和一些官方插件不仅容易受到攻击,而且很多情况下,创建启动项目会用到代码生成器,所以 IntelliJ 生成的项目也容易受到攻击。
Gradle
Gradle 是一个有趣的案例。作为 Gradle 的贡献者,你不会受到此漏洞的影响。但是,当在 Gradle 公司的 Team City CI 基础设施上,用 Gradle 构建 Gradle 代码库时,该基础设施会覆盖一些默认值,改成用 HTTP 从 JFrog Artifactory 实例上获取工件。幸运的是,该基础设施与 Gradle JFrog Artifactory 服务器位于同一网络中。
话又说回来,Gradle 公司的 JFrog Artifactory 服务器还通过 HTTP 为其他工件服务器建立镜像,因此可能会将这些镜像暴露给基于 MITM 的缓存中毒(cache poisoning)攻击。
Elastic Search
在撰写本文时,Elastic Search 代码库有 3.86 万次加星,是 GitHub 上最受瞩目的 Java 项目。Elastic Search 的主工程有超过 1100 个贡献者。因此我确定,Elastic 构建里的测试逻辑部分易受此漏洞影响。
Apache
我在 Apache 软件基金会(Apache Software Foundation)的几个项目中发现了这个漏洞。
其中值得注意的将在下文列出。
截至本文发布之日,Apache 软件基金会已决定不给受影响项目发布 CVE 编号。虽然这些项目大多没有统计过是否被此漏洞攻陷。
Groovy 编译器
Apache Groovy 是在 JVM 上做开发最流行的替代品之一,也容易受到攻击。在撰写本文时,根据Tiobe索引,Groovy 在世界编程语言中受欢迎程度排名第 19。与 Kotlin 类似,Groovy 编译器的 buildscript 类路径有通过 HTTP 解析的依赖关系。这也导致了工件被破坏的可能性。
幸运的是,构建 Groovy 编译器的引导编译器完全用 Java 编写,因此受“Trusting Trust”攻击的可能性非常小。
此外,我还发现 Groovy 的 Eclipse 插件易受攻击。
Hadoop
Apache Hadoop 有超过 193 个贡献者,成为贡献者最多的项目。所有贡献者在他们机器上运行的 Hadoop 构建都可能被 MITM 攻陷。
Kafka
Apache Kafka 最初由 LinkedIn 团队编写,是一个快速的事件代理。LinkedIn 在内部使用 Kafka,每天摄取的信息条数已经超过1万亿条。我发现 Kafka 的构建系统是通过 HTTP 而不是 HTTPS 加载 Gradle 插件。
Apache 其他项目
我发现,易受攻击的 Apache 项目包括但不限于以下项目:Casandra、Geode、Storm、Bigtop、Fink、OpenJPA、Royal Compiler 和 Airavata。
Jenkins
Jenkins 在全球有超过 100 万用户,是使用最广泛的开源自动化服务器。
Jenkins 被用作自托管的 CI 管道,用于自动构建和测试软件。Jenkins 和许多 Jenkins 官方插件都附带了通过 HTTP 下载的依赖项。
Spring
我在 spring-security-oauth 项目发现了第一个(漏洞)位置。Spring 项目是我开始研究基于 Maven 的第一个项目,因此不得不找了一种完全不同的搜索方法,用来通过 GitHub 搜索功能检查 Maven POM 文件。我一开始寻找,就发现这个漏洞存在于 Spring 组织下的许多其他项目中。
Spring 团队立即做出响应,开始修补他们的所有项目。因受 Pivotal 项目重度影响,Pivotal 开发了一个工具,用以查找和替换整个代码库中所有使用 HTTP 的地方。该工具在此:
Red Hat
这个漏洞还影响了 Red Hat 维护的许多项目。这些项目包括但不限于 Hibernate ORM、RestEasy 和 Wildfly(旧称 JBoss)生态系统中的许多项目。
Eclipse 基金会
与 Red Hat 和 Apache 基金会类似,漏洞也影响了 Eclipse 基金会的项目 Vorto、Buildship、xtext、Orion 和 Birt。
Oracle
此漏洞还影响了 Oracle 的一些开源项目,包括 VisualVM、PGQL、OpenGrok 和 Helidon。
测试库和框架
一些非常流行的 JVM 测试库和框架也很容易受到攻击,包括 TestNG、Spock 和 PowerMock。
其他项目
其他项目,包括 Grails、FasterJacksonXML 和 Ehcache3 的 Scala 模块也发现了此漏洞。此外,我在网飞(Netflix)、谷歌、Twitter、美国国家安全局(NSA)、Stripe、Gluon(Scene Builder)PortSwigger、Black Duck、Snyk、LinkedIn 和 PayPal 的开源项目中也发现了这个漏洞。讽刺的是,当我向 PayPal 的安全团队提交问题报告漏洞时,他们关闭了这个问题,因为他们认为 MITM “超出了他们的HackerOne的计划范围”。
MITM 的常见程度如何?
我对常见的 MITM 的初步研究,源于我研究恶意破坏 XML 的解析器。这些解析器加载了通过 HTTP 加载的 DTD 文件,以实现XXE。相关信息以后找机会另写文章详述。我发现 MITM 的常见程度相当惊人。
互联网服务提供商
令我惊讶的是,互联网服务提供商(ISP)似乎经常做 MITM。
康卡斯特开始向我在互联网上请求的页面注入 400 多行 JavaScript 代码,以便当浏览器渲染页面时,用 JavaScript 生成弹出窗口,向我推销新的调制解调器。
印度的Bharat Sanchar Nigam Limited(BSNL)也曾将广告注入用户通过 HTTP 访问的网页。
这就说明,MITM 的基础条件早已具备,稍加利用就能影响 JAR 文件。
恶意行为者
当恶意行为者获得对系统的访问权限时,他们通常会迅速利用新的立足点来建立 MITM。Verizon 每年都会发布数据泄露调查报告(Data Breach Investigation Report,DBIR),分析每年最常被利用的各种攻击媒介。下面引用自他们的一份报告。
行为
三大威胁行为是黑客入侵、恶意软件和社会工程学。最常见的黑客入侵行为的类型是使用被盗的登录凭据、利用后门程序和中间人(man-in-the-middle,即 MITM 的全写)攻击。
引用自: Verizon 2011数据违规调查报告;第 69 页
引用自该报告的分析:
我推测,一旦黑客已在系统中立足,MITM 就比较次要了。但荷兰高科技犯罪部门的数据显示,MITM 本身就值得关注的——在他们统计到的 32 个数据泄露事件中,有 15 个涉及 MITM 行为。
应把 MITM 攻击视为软件安全中确定存在的威胁。
通过公共 WiFi 连接
抛开所有其他攻击媒介,开发人员通过公共 WiFi 从事这些项目的工作,都等同于放开了他们的电脑,可能会受到恶意攻击。这种攻击可以用著名的 Firefox 插件Firesheep演示。
WiFi 热点的潜在危害可能会对开发人员产生重大影响,因为他们很多人会在咖啡店、开发者大会等地方使用公共 WiFi。只需要WiFi Pineapple,加上他们电脑未缓存过的依赖就可以感染他们的电脑了。
政府机构
拜斯诺登所赐,我们现在知道了美国政府三个邮件中介(letter agencies)对美国公民进行 MITM 攻击的各种方法。
NSA 靠跟美国电信公司的秘密合作伙伴关系,诱使目标访问 FoxAcid 服务器。作为 Turmoil 系统的一部分,NSA 将秘密服务器(代号为 Quantum)放置在互联网骨干网的关键位置,确保它们可以比其他网站更快响应。靠这个速度差,这些服务器可以在合法网站响应之前,模拟成目标要访问的网站,从而欺骗目标的浏览器访问 Foxacid 服务器。
以前,由于BGP路由配置错误,美国国内的互联网流量也有过通过国外路由的历史。
通过 HTTP 加载的最常见的存储库
让我们看一下使用 HTTP 的最常见代码库的一些统计信息。请注意,这些数字并不精确,可能略微大些,因为 GitHub 的搜索本质上是模糊搜索。
Maven 中央仓库(Maven Central)
Maven 中央仓库是 JVM 生态系统中最常用的工件服务器,是 Maven 使用的默认工件服务器,是 JVM 工件托管空间中的第一个主要参与者。
JFrog JCenter
JCenter 是 Maven 中央仓库的超集。开发人员把他们的工件发布到 JFrog Bintray 后,可以请求在此创建他们工件的镜像。
JFrog Bintray
JFrog Bintray 让开发人员可以为开源项目免费创建自己的工件服务器。
由Sheetsu.com提供
显然,这是 Gradle 和 Maven 项目中广泛存在的一个安全漏洞。
编写一个(理论上的)Java 库蠕虫
作为一个思维实验,我草拟了滥用此 MITM 漏洞来创建 Java 库蠕虫的理论。实验结果参见此处:
若你觉得“太长不看”,简单说来如下图:
该漏洞的后果是,在发布期间使用有 MITM 漏洞的依赖,可能会让恶意代码攻陷构建出来的工件,从而感染下游用户。
修复过去
对于已经发布的库,除非这些项目构建是完全可重复的,否则无可奈何。
对于易受此影响的编译器(Kotlin)、用于测试自己的测试库(Spock 和 TestNG)、用于构建自己的构建工具(Gradle),这可能是一个问题,因为信任链已被打破。大多数编译器都用于编译自己。有关此主题的更多信息,请参阅 Ken Thompson 写的论文《Reflections on Trusting Trust》,这篇相对较短。
修复未来
我认为像 Gradle、Maven 和 SBT 这样的构建工具,应该默认用 HTTPS 解析依赖关系,除非用户明确声明不用 HTTPS。这将迫使用户明确声明使用不安全的协议,从而防止不小心写错。我向 Gradle 和 Maven 都提出了要求实现该功能的提案,链接如下,请帮我投个赞成票!
Deprecate HTTP Download by JLLeitschuh · Pull Request #9419 · gradle/gradle
[MNG-6673] Deprecate HTTP Download & Upload - ASF JIRA
代码仓库的自动审查
Pivotal 开发的 nohttp 工具可以查找所有出现的 HTTP,除了列入白名单的 HTTP(比如 XML 命名空间名称)。这将确保 HTTP 不会在其他地方引起问题(比如 Gradle Wrapper 位置,DTD 声明等)。它可以寻找和替换“HTTP”,并集成在构建里,确保将来不使用 HTTP。
工件主机将在 2020 年 1 月弃用 HTTP
随着此漏洞的范围变得越来越大,我很快意识到,修复这个漏洞的一些责任在 Maven Central 和 JCenter 等工件主机上。我联系了两个最大的工件主机 Sonatype(Maven Central)、JFrog(JCenter),以及 Pivotal(Spring)、Eclipse Foundation、Jenkins、Red Hat 和 JetBrains 等较小的主机,询问它们是否愿意从 2020 年 1 月 15 日起完全阻止通过 HTTP 发出的下载请求。
25%的 Maven Central 下载仍在使用 HTTP
JFrog 和 Pivotal 很快就都告诉我他们会跟进。
个人容易受到这种影响吗?
鉴于我发现许多广泛使用的开源项目都存在这个漏洞,我建议任何开发 JVM 软件的人,都应检查代码库的构建逻辑,以解决使用 HTTP 依赖的问题。
我需要找什么?
对于 Gradle,你应该寻找像这样的代码库配置。
Gradle 开发人员可能还应检查所有 init.gradle 脚本,它们在其公司内部分发,但一般不检入源代码管理系统。
对于基于 Maven 的项目,你要寻找像这样的代码库配置。
Maven 开发人员还应该检查~/.m2/settings.xml 文件中的配置,因为代码库凭据通常就存在这里。
此外,JFrog Artifactory 或 Sonatype 的 Nexus 的企业用户,应检查其服务器的配置,看它们是否通过 HTTP 创建了其他工件服务器的镜像。
在 JFrog Artifactory 中不安全的配置的例子
我已经联系了 Sonatype 和 JFrog,要求他们在未来的更新中,对不安全的配置向用户/管理员提出警告。
JFrog 回应说,该功能已正式成为他们路线图的一部分,但还没有计划发布日期。
如果发现此漏洞,该怎么办?
要把执行潜在恶意 Jar 的整个机器(开发机、构建机等)看成可能被攻陷了。这也意味着该机器可以访问的任何内容(其他项目、凭据、其他主机等)也应该视为可能被攻陷了。共享工件的缓存,如~/.gradle 和~/.m2 目录,其中的 Gradle 和 Maven 的缓存工件都应视为已受污染,应该删除。为避免单个易受攻击的应用程序连累其构建的每个项目,最佳做法是隔离构建。
如果你是易受此攻击的开源项目的维护者,那么你有责任审查先前的版本,或提交 CVE 编号,通知下游用户可能存在的危害。
结束语
不幸的是,我只能联系受此漏洞影响的一小部分项目。你在工作中依赖的许多开源项目可能都容易受此影响。如果你可以,请联系你发现的受影响项目,一起保护 Java 生态系统。
如果你使用开源软件,或用这些构建工具开发商业软件,我强烈建议:为整个软件供应链的安全,请审查你的构建。
特别鸣谢
感谢我联系的所有组织中,所有真正令人敬畏的专业安全团队和项目维护人员。我自己是绝不可能修补所有地方的。其中一些团队,在我报告后几小时内就做出回应,并在第二天就推出修复程序。另外,我要感谢Snyk愿意做这些 CVE 报告的 CNA。我还要感谢 Max Veytsman 创造了 Dilettante。用 POC 使披露流程更加顺畅。
深入研究的附注
如果你是一名安全研究人员,并希望在其他 Java 项目中发现这些安全漏洞,我可能漏了说明我在 Github 搜索时用的查询条件,补充如下:
在 GitHub 搜索功能里查询漏洞时可用以下查询条件
在 GITHUB_ORGANIZATION 的 Gradle 构建文件里搜索使用“http://”之处
在工程的源代码里搜索代码仓库的通用查询条件
在 Gradle Builds 里搜索的特殊查询条件
在 Maven POM 文件里搜索的特殊查询条件
如果你用这些查询条件在某项目中发现此问题,请指回本文。
检查发布版本异同的工具
原文链接:
评论