rmi_jndi
简介
RMI(Remote Method Invocation)
即Java
远程方法调用,RMI
用于构建分布式应用程序,RMI
实现了Java
程序之间跨JVM
的远程通信。
(来自[RMI · 攻击Java Web应用-Java Web安全] (javasec.org))
RMI客户端
在调用远程方法时会先创建Stub(sun.rmi.registry.RegistryImpl_Stub)
。Stub
会将Remote
对象传递给远程引用层(java.rmi.server.RemoteRef)
并创建java.rmi.server.RemoteCall(远程调用)
对象。RemoteCall
序列化RMI服务名称
、Remote
对象。RMI客户端
的远程引用层
传输RemoteCall
序列化后的请求信息通过Socket
连接的方式传输到RMI服务端
的远程引用层
。RMI服务端
的远程引用层(sun.rmi.server.UnicastServerRef)
收到请求会请求传递给Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)
。Skeleton
调用RemoteCall
反序列化RMI客户端
传过来的序列化。Skeleton
处理客户端请求:bind
、list
、lookup
、rebind
、unbind
,如果是lookup
则查找RMI服务名
绑定的接口对象,序列化该对象并通过RemoteCall
传输到客户端。RMI客户端
反序列化服务端结果,获取远程对象的引用。RMI客户端
调用远程方法,RMI服务端
反射调用RMI服务实现类
的对应方法并序列化执行结果返回给客户端。RMI客户端
反序列化RMI
远程方法调用结果。
造成漏洞的真正原因:
当 RMI Registry 接收到客户端的
bind
,rebind
或lookup
等请求时,它会自动反序列化客户端发送过来的对象数据。如果服务器端的 classpath 中恰好存在一个含有漏洞的第三方库(例如本例中的commons-collections
),那么攻击者就可以构造一个恶意的序列化对象,在服务器反序列化它时触发漏洞,执行任意代码。也就是说当bind,或者吧rebind,lookup 的时候都会出现
- RMI Registry 是一栋大楼前台的访客登记簿。
- RMI Server 是一家公司,它派了一名员工(创建的
Remote
对象)来这栋大楼提供服务。这名员工需要到前台,在登记簿上写下:“我是‘计算服务’公司的,找我请拨分机号 XXX”。这个“登记”的动作就是bind()
。 - RMI Client 是一个访客,他想找“计算服务”。他来到前台,翻开登记簿(
lookup("计算服务")
),找到了分机号 XXX,然后直接联系这位员工。
漏洞就出在这个“登记簿”上:
这个登记簿(RMI Registry)默认是完全开放的,它不会验证前来登记的人(调用
bind
/rebind
的程序)的身份。任何能够通过网络连接到前台的人,都可以在登记簿上写下任何内容。
谁都可以注册
rmi 流程
但是这个地方rmi 连接出现了很大的问题,导致一直连接不上。
但吃尝试了exp 又可以实现攻击…..
下面是部分代码
RMIServerTest.java
1 |
|
RMIClientTest.java
1 |
|
RMITestImpl.java
1 |
|
RMITestInterface.java
1 |
|
rmi 反序列化
RMI
通信中所有的对象都是通过Java序列化传输的,在学习Java序列化机制的时候我们讲到只要有Java对象反序列化操作就有可能有漏洞。
既然RMI
使用了反序列化机制来传输Remote
对象,那么可以通过构建一个恶意的Remote
对象,这个对象经过序列化后传输到服务器端,服务器端在反序列化时候就会触发反序列化漏洞。
首先我们依旧使用上述com.anbai.sec.rmi.RMIServerTest
的代码,创建一个RMI
服务,然后我们来构建一个恶意的Remote
对象并通过bind
请求发送给服务端。
RMI客户端反序列化攻击示例代码:
1 |
|
最根本的问题还是利用了cc1 中的AnnotationInvocationHandler 类
Apache_Commons_Collections1分析 - AU9U5T
sun.reflect.annotation.AnnotationInvocationHandler
类实现了java.lang.reflect.InvocationHandler
(Java动态代理
)接口和java.io.Serializable
接口,它还重写了readObject
方法,在readObject
方法中还间接的调用了TransformedMap
中MapEntry
的setValue
方法,从而也就触发了transform
方法,完成了整个攻击链的调用。
程序执行后将会在RMI服务端
弹出计算器(仅Mac系统,Windows自行修改命令为calc
),RMIExploit
程序执行的流程大致如下:
- 使用
LocateRegistry.getRegistry(host, port)
创建一个RemoteStub
对象。 - 构建一个适用于
Apache Commons Collections
的恶意反序列化对象(使用的是LazyMap
+AnnotationInvocationHandler
组合方式)。 - 使用
RemoteStub
调用RMI服务端
的bind
指令,并传入一个使用动态代理创建出来的Remote
类型的恶意AnnotationInvocationHandler
对象到RMI服务端
。 RMI服务端
接受到bind
请求后会反序列化我们构建的恶意Remote对象
从而触发Apache Commons Collections
漏洞的RCE
。
问题
为什么在exp 中没有实现remote 还可以bind?
因为通过了动态代理的方式实现了remote
1
2
3
4// 使用动态代理创建出Remote类型的Payload
Remote remote = (Remote) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(), new Class[]{Remote.class}, annHandler
);Java 的
Proxy.newProxyInstance()
方法是一个强大的工具,它允许你在程序运行时创建一个“代理”对象,这个代理对象可以实现任何你指定的接口组合。
RMI-JRMP 反序列化漏洞
JRMP
接口的两种常见实现方式:
JRMP协议(Java Remote Message Protocol)
,RMI
专用的Java远程消息交换协议
。IIOP协议(Internet Inter-ORB Protocol)
,基于CORBA
实现的对象请求代理协议。
由于RMI
数据通信大量的使用了Java
的对象反序列化,所以在使用RMI客户端
去攻击RMI服务端
时需要特别小心,如果本地RMI客户端
刚好符合反序列化攻击的利用条件,那么RMI服务端
返回一个恶意的反序列化攻击包可能会导致我们被反向攻击。
我们可以通过和RMI服务
端建立Socket
连接并使用RMI
的JRMP
协议发送恶意的序列化包,RMI服务端
在处理JRMP
消息时会反序列化消息对象,从而实现RCE
。
1 |
|