Sonatype已经对此帖做出了相应的反应,并在他们的博客上声明将会对所有的用户开启SSL链接。这变化让Java程序的生态系统变得更加安全可靠,我为此感到非常高兴。
虽说如此,但如果当你看到这个博客,并考虑花10美元来评估你的产品是否需要这个安全性的需要时,答案是否定的。这就好比汽车公司决定花10美元来评估他们的汽车是否需要气囊。幸运的是,现实生活中汽车公司必须安装气囊。
Sonatype的这次机制修改令我感到很开心,希望他们能够继续减少安全性与产品的冲突。对于我们开发者而已,为用户提供最安全的最有保障的产品是我们的责任。用户对于不能处理好产品与安全性的容忍度能有多大。
有一天,我看到我的防火墙是这么设置的,于是我开始对我的一个Clojure项目进行入侵:
然后我通过80端口在 http//repo.maven.apache.org下载了clojure.jar。这意味着我将能够通过一个未加密的http来下载包。我一开始认为这是leiningen的问题。但结果完全不是。Clojure.jar和其他许多的jar包在Java/Clojure/Scala领域都很重要,他们都会被正式的放在Sonatype提供的一个公共服务 - Maven Central。Sonatype有一个机制:只有那些拥有身份标识的人才允许SSL链接。你需要通过捐给Apache基金会十美元,才能获得身份标识和SSL链接。如果你不相信我说的话,捐赠的网页在这,此机制在此博客上声明。这里面甚至提到了中间人攻击。
因为身份标识是针对个人或者组织分发,所以像maven或者leiningen这些工具不能绑定它。如果你使用这些工具,那么下载java程序或者安装它的一些依赖不会通过SSL。经过我在twitter和Sonatype的一个员工对此事进行了争论,确认了此事。
你下载的jar包会来自中间人的,并且你执行的代码会被恶意软件替换过。要想阻止这类的事情发生,你需要小心的执行每一步操作,稍有不慎就会支付别人十美元。
通常什么时候会发生这类事情?如果你曾链接过咖啡店的公共网络或者别人的无线网络,中间人就能和你通信了。你的网络服务提供者应能够随意的跟你通信,有的人这么做是为了打广告。或者,你也会还会遭受来自国家操纵的这类攻击。
Dilettante
为了证明这有多么容易,我写了一个中间人代理dilettante,它功能是拦截来自于maven central的JAR包然后再注入恶意代码进去。
通过dilettante来代理http的传输,然后给任何在maven central下载的JAR包设置后门。那个设置了后门的版本同时会保留原有的功能,但是当他们使用的这个类库的时候,将会提示一个友好的信息。你可以看这个视频:
或者截图:
源代码在这。
具体实现
JAR包从maven central 下载的时候,由于是通过HTTP进行传输的,因此中间人可以随意的替换。JAR包是可以被标识的,但是根据我对于标准工具使用的经验,这些标识是不会被检查的。其他唯一的方法可以验证一个也是通过HTTP传输的SHA1和。当dilettante发现有JAR来自maven central,它将会把一个拥有后门的版本来替换成原来的。然后替换后的版本将会在受害者的电脑上运行恶意的代码。由于SHA1仅是通过HTTP传输,那么dilettante会简单地将它所能看到的所有哈希表替换成对应的拥有后面JAR包的哈希表。
我用优秀的mitproxy类库来创建我的工具。我先为代理写一个inline script,然后又用libmproxy创建一个独立的工具。
package dilettante; public class Dilettante { public static void() { // do some evil stuff } }
JAR包只是一个包含资源,类和元数据的压缩包。我通过添加自己的类到JAR,从而给这个JAR设置了后门。
稍微头疼的是想办法如何调用我的恶意代码。我知道受害者肯定会下载一些类库。然而我需要不管用户调用类库中的哪个类,它都能运行我的代码,并且还不能影响以前类库的正常使用。
Java有静态类块这一概念,它能在类级别进行初始化。当类加载到内存的时候,静态类里面的代码会只被调用这一次。在我把恶意的类插入到Jar包之后,我就能像下面这样在静态块里面调用代码:
import dilettante.*; static { Dilettante.backdoor(); }
为了插入上面的代码,我需要将它直接插入到Java class里面,而不是source文件。我是使用Karakatau将代码以Jasmin的格式插入进去。Karakatau是一个针对Python的Java 反编译/编译类库。
.method static <clinit> : ()V ; method code size: 4 bytes .limit stack 0 .limit locals 0 invokestatic dilettante/Dilettante backdoor ()V return .end method
局限性
这只是个概念化的试验,同时它还是存在局限性。
1.目前由于一些原因,它还不是很快:
我必须进行反编译和编译。如果能够直接插入编译好的代码到类里面,那就更高效了。
我实际上是通过使用Python的zipfile类库将压缩包的类文件都创建了个副本。从空间和速度方面而言,这是不高效的。如果能够更加了解zip的功能,可能会找到一个高效插入数据的方法。
2.如果用户同时下载多个JAR包。那么我将会逐个设置后门。恶意的代码在每个JAR只会被执行一次。但是如果多个jar被设置了后门,那么它将会被执行多次。如果我们将那个猫的图片替换成一个高质量,稳定的后门并且它能够只影响系统一次,那么这个问题就不会再出现。