本文学习如何绕过JEP290反序列化限制
关于JEP290
JEP290是Java底层为了缓解反序列化攻击提出的一种解决方案,主要做了以下几件事
- 提供一个限制反序列化类的机制,白名单或者黑名单。
- 限制反序列化的深度和复杂度。
- 为RMI远程调用对象提供了一个验证类的机制。
- 定义一个可配置的过滤机制,比如可以通过配置properties文件的形式来定义过滤器。
JEP290的实际限制
写一个RMIServer
RMIServer.java
1package org.chabug.rmi.server;
2
3import java.rmi.Naming;
4import java.rmi.registry.LocateRegistry;
5
6public class RMIServer {
7 public static String HOST = "127.0.0.1";
8 public static int PORT = 1099;
9 public static String RMI_PATH = "/hello";
10 public static final String RMI_NAME = "rmi://" + HOST + ":" + PORT + RMI_PATH;
11
12 public static void main(String[] args) {
13 try {
14 // 注册RMI端口
15 LocateRegistry.createRegistry(PORT);
16 // 创建一个服务
17 Hello hello = new HelloImpl();
18 // 服务命名绑定
19 Naming.rebind(RMI_NAME, hello);
20
21 System.out.println("启动RMI服务在" + RMI_NAME);
22 } catch (Exception e) {
23 e.printStackTrace();
24 }
25 }
26}
HelloImpl.java
1package org.chabug.rmi.server;
2
3import java.rmi.RemoteException;
4import java.rmi.server.UnicastRemoteObject;
5
6public class HelloImpl extends UnicastRemoteObject implements Hello {
7 protected HelloImpl() throws RemoteException {
8 }
9
10 public String hello() throws RemoteException {
11 return "hello world";
12 }
13
14 public String hello(String name) throws RemoteException {
15 return "hello" + name;
16 }
17
18 public String hello(Object object) throws RemoteException {
19 System.out.println(object);
20 return "hello "+object.toString();
21 }
22}
Hello.java
1package org.chabug.rmi.server;
2
3
4import java.rmi.Remote;
5import java.rmi.RemoteException;
6
7public interface Hello extends Remote {
8 String hello() throws RemoteException;
9 String hello(String name) throws RemoteException;
10 String hello(Object object) throws RemoteException;
11}
使用JDK7u21打Commonscollections1,成功弹出calc。
使用JDK8u221启动RMIServer攻击失败
报错显示
1ObjectInputFilter REJECTED: class sun.reflect.annotation.AnnotationInvocationHandler, array length: -1, nRefs: 8, depth: 2, bytes: 298, ex: n/a
JEP290的过滤机制
在上文的报错中可见sun.reflect.annotation.AnnotationInvocationHandler
被拒绝,跟一下RMI的过程,看下在哪里过滤了,JEP290又是怎么实现的。以下使用JDK8U221调试
首先我们要清楚RMI的实现流程
在远程引用层中客户端服务端两个交互的类分别是RegistryImpl_Stub
和RegistryImpl_Skel
,在服务端的RegistryImpl_Skel
类中,向注册中心进行bind、rebind操作时均进行了readObject操作以此拿到Remote远程对象引用。
跟进63行进入到java.io.ObjectInputStream#readObject
,然后进入readObject0()
在readObject0()之中进入readOrdinaryObject()
继续进入readClassDesc()
进入readProxyDesc()
在readProxyDesc()中有filterCheck
先检查其所有接口,然后检查对象自身。进入filterCheck()之后
调用了serialFilter.checkInput(),最终来到sun.rmi.registry.RegistryImpl#registryFilter
1return String.class != var2 && !Number.class.isAssignableFrom(var2) && !Remote.class.isAssignableFrom(var2) && !Proxy.class.isAssignableFrom(var2) && !UnicastRef.class.isAssignableFrom(var2) && !RMIClientSocketFactory.class.isAssignableFrom(var2) && !RMIServerSocketFactory.class.isAssignableFrom(var2) && !ActivationID.class.isAssignableFrom(var2) && !UID.class.isAssignableFrom(var2) ? Status.REJECTED : Status.ALLOWED;
没有给AnnotationInvocationHandler
白名单,所以返回REJECTED。
绕过JEP290
在RMI远程方法调用过程中,方法参数需要先序列化,从本地JVM发送到远程JVM,然后在远程JVM上反序列化,执行完后,将结果序列化,发送回本地JVM,而在本地的参数是我们可以控制的,如果向参数中注入gadget会怎么样?
我在HelloImpl实现了三个hello()方法,分别是void、string、Object类型的参数
在客户端我向Object参数类型注入cc5的gadget
运行成功弹出calc
也就是说:如果目标的RMI服务暴漏了Object参数类型的方法,我们就可以注入payload进去。
那么别的参数类型呢?在sun.rmi.server.UnicastRef#unmarshalValue中判断了远程调用方法的参数类型
如果不是基本类型,就进入readObject,之后的流程也走了filterCheck过滤
不过在sun.rmi.transport.DGCImpl#checkInput
这里ObjID是在白名单中的,所以可以被反序列化。
那这个只是object类型的参数可以,其他的参数类型呢?
由于攻击者可以完全控制客户端,因此他可以用恶意对象替换从Object类派生的参数(例如String)有几种方法:
- 将java.rmi软件包的代码复制到新软件包,然后在其中更改代码
- 将调试器附加到正在运行的客户端,并在序列化对象之前替换对象
- 使用Javassist之类的工具更改字节码
- 通过实现代理来替换网络流上已经序列化的对象
afanti师傅用的是通过RASP hook住java.rmi.server.RemoteObjectInvocationHandler
类的InvokeRemoteMethod
方法的第三个参数非Object的改为Object的gadget。他的项目地址在RemoteObjectInvocationHandler。
修改src\main\java\afanti\rasp\visitor\RemoteObjectInvocationHandlerHookVisitor.java
的dnslog地址,然后打包出来在RMIClient运行前加上-javaagent:e:/rasp-1.0-SNAPSHOT.jar
虽然报错参数类型不匹配
但是dnslog已经收到请求了。
参考
- https://mogwailabs.de/blog/2019/03/attacking-java-rmi-services-after-jep-290/
- https://www.anquanke.com/post/id/200860
- https://github.com/Afant1/RemoteObjectInvocationHandler
- https://paper.seebug.org/454/
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。
评论