深入理解POJONode的toString调用任意类getter方法原理

一、前言

1.1 说说废话

由于最近刚入门java安全,学了CC链、CB链,想要去找一些CTF反序列化题目练练手,然后突然发现,原来CC链、CB链不是尽头,只是开始,还有一大堆的链子等着我去结合CC、CB链进行利用;

通过wp学习反序列化利用的过程中,发现POJONode#toString真的是高频出现啊,真不理解这是啥玩意儿,咋这么好用呢?通过一顿资料搜索发现,原来是Jackson的原生反序列化链子之一,之前学Jackson的时候没看到这个,现在补上;

这里就不在赘述Jackson反序列化的基础知识了,直接分析相关类的内容;

1.2 环境

  • jdk8u65
  • jackson-databind 2.7.9
  • jackson-core 2.7.9
  • jackson-annotations 2.7.9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.9</version>
</dependency>

1.3 利用限制

存在jackson环境,目前了解对版本无要求、对jdk版本也无要求

二、POJONode#toString

2.1 方法执行流程分析

首先,说一下POJONode本身是没有重写toString()方法的,所以它调用的toString()方法是它的父类的toString()方法;

跟进父类ValueNode,发现也没有重写toString(),因此我们继续跟进ValueNode的父类BaseJsonNode类,可以看到toString()方法的具体实现。

继续跟进nodeToString()方法,查看具体实现;可以发现其调用了writeValueAsString()方法进行序列化

总结一下,POJONode#toString方法执行了对自身对象的序列化;

2.2 Jackson序列化流程

BaseJsonNode#toString -> InternalNodeMapper#nodeToString -> ObjectWriter#writeValueAsString -> ObjectWriter#_writeValueAndClose -> ObjectWriter#serialize -> DefaultSerializerProvider#serializeValue -> DefaultSerializerProvider#serialize -> DefaultSerializerProvider#_serialize -> SerializableSerializer#serialize -> POJONode#serialize -> SerializerProvider#defaultSerializeValue -> JsonSerializer#serialize -> BeanSerializer#serialize - > BeanSerializerBase#serializeFields -> BeanPropertyWriter#serializeAsFields

在跟这条序列化链子的过程中,发现确实是复杂到了极点,还是不跟了吧。
上述知识找到了调用getter的链子,它会调用传入POJONode的对象的所有属性的getter函数,从而实现任意getter调用;

具体的对象属性如何获取和getter方法的获取还要继续分析findTypedValueSerializer函数,这边就不分析了,知道结论和用法就结束吧!

三、POJONode反序列化Gadget

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) throws NotFoundException, CannotCompileException {
ToGetterClass toGetterClass = new ToGetterClass();

//删除 BaseJsonNode#writeReplace 方法用于顺利序列化
ClassPool pool = ClassPool.getDefault();
CtClass ctClass0 = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod writeReplace = ctClass0.getDeclaredMethod("writeReplace");
ctClass0.removeMethod(writeReplace);
ctClass0.toClass();

POJONode pjNode = new POJONode(toGetterClass);
}

参考链接

  1. https://xz.aliyun.com/t/12509
  2. https://xz.aliyun.com/t/12966

深入理解POJONode的toString调用任意类getter方法原理
http://candyb0x.github.io/2024/12/09/深入理解POJONode的toString调用任意类getter方法原理/
作者
Candy
发布于
2024年12月9日
更新于
2025年8月13日
许可协议