编者按:2015年11月6日,国外 FoxGlove安全研究团队于在其博客上公开了一篇关于常见 Java应用如何利用反序列化操作进行远程命令执行的文章。Java在进行反序列化操作的时候会使用ObjectInputStream
类调用 readObject()
方法去读取传递过来的序列化对象字节流进行处理,利用 Apache Commons Collections 库恰好可以构造出了一个在反序列化操作时能够自动执行命令的调用链。如果服务端程序没有对用户可控的序列化代码进行校验而是直接进行反序列化使用,并且程序中运行一些比较危险的逻辑,就会触发一些意想不到的漏洞。该漏洞在最新版的WebLogic、WebSphere、JBoss、Jenkins、OpenNMS中都可实现远程代码执行。 \
漏洞爆发后国内主要安全机构都作了相关分析,本文转自绿盟科技博客。 \
正文
\前一段时间热炒的Java反序列化漏洞,大家在玩的很嗨的时候貌似忽略了一件很重要的事情——Java在CS架构的设计中使用序列化传输是非常普遍的现象,而在像JBoss这种中间件也使用这种设计。所以,我在一边研究这个漏洞,一边看大家嗨嗨的玩的同时,也很好奇在一些通过Java实现的CS架构应用(比如:大型国企都喜欢用的会计软件、内容发布系统),是不是也会用到Apache Commons Collections这个库。 \
不知道是不是研究Java Web的大神们都在闷声发大财,这次这个漏洞的分析文章大多都停留在那个老外blog中的各个中间件的利用玩法上,却没有注意到Java Web中常见的架构都会因为这个问题而沦陷。而且除了长亭之外的文章,其他各家的修复建议大多都是针对利用来进行修复,治标不治本。 \
0x01 大规模利用原罪——RMI
\在分布式遍地走的如今,很多使用Java开发的Web也都使用了分布式分发的结构,比如我所了解的很多大型组织都会在后台部署一些Java应用,用于向对外网站发布更新的静态页面,而这种发布命令的下达使用的就是RMI。 \
我们先看下RMI在wikipedia上的描述: \
\\Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Java编程语言里,一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。 \
Java RMI极大地依赖于接口。在需要创建一个远程对象的时候,程序员通过传递一个接口来隐藏底层的实现细节。客户端得到的远程对象句柄正好与本地的根代码连接,由后者负责透过网络通信。这样一来,程序员只需关心如何通过自己的接口句柄发送消息。
更加令人警示的是RMI的传输过程必然会用到序列化和反序列化,那么如果RMI服务端接口对外开放,并且服务端使用了像Apache Commons Collections这样的库,很容易被攻击者窥视。 \
0x02 被忽略掉的关键内容
\breenmachine的原文中,有不少的地方描述了关于反序列化漏洞对于RMI的影响,比如: \
\\Java LOVES sending serialized objects all over the place. For example: \
- In HTTP requests – Parameters, ViewState, Cookies, you name it. \
- RMI – The extensively used Java RMI protocol is 100% based on serialization. \
- RMI over HTTP – Many Java thick client web apps use this – again 100% serialized objects. \
- JMX – Again, relies on serialized objects being shot over the wire. \
- Custom Protocols – Sending an receiving raw Java objects is the norm – which we’ll see in some of the exploits to come.
RMI的传输100%基于反序列化。 \
还有这个: \
\\If you see port 1099, that’s Java RMI. RMI by definition just uses serialized objects for all communication. This is trivially vulnerable, as seen in our OpenNMS exploit.
如果你看到了1099端口,这是Java RMI的默认端口。RMI默认使用序列化来完成所有的交互。这是非常常见的漏洞,就像我们写出的OpenNMS exploit。 \
《Exploit 5 – OpenNMS through RMI》这个小节,都是在介绍RMI的利用情况,但是都被大家忽略掉了,这令我很费解。 \
0x03 Exploit的构造
\RMI的Exploit构造相对比较容易,对于了解Java编程的同学应该很简单的就可以写出来了。这里我们简单的来分析一下ysoserial这个工具中对于RMI利用的实现。
public class RMIRegistryExploit {\ public static void main(final String[] args) throws Exception {\ // ensure payload doesn't detonate during construction or deserialization \ ExecBlockingSecurityManager.wrap(new Callable\u0026amp;lt;Void\u0026gt;(){public Void call() throws Exception {\ Registry registry = LocateRegistry.getRegistry(args[0], Integer.parseInt(args[1])); \ String className = CommonsCollections1.class.getPackage().getName() + \".\" + args[2];\ Class\u0026lt;? extends ObjectPayload\u0026gt; payloadClass = (Class\\\\\\\\\u0026lt;? extends ObjectPayload\u0026gt;) Class.forName(className);\ Object payload = payloadClass.newInstance().getObject(args[3]);\ Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(\"pwned\