jackson的二次反序列化分析
字数 1504 2025-08-29 22:41:32
Jackson二次反序列化分析与利用
1. Jackson反序列化基础
1.1 Jackson反序列化特点
Jackson在CTF比赛中被广泛使用的原因:
- Spring Boot框架自带Jackson依赖
- 反序列化过程没有内置黑名单限制
- 可以调用任意getter方法
1.2 基本利用方式
常用方法:
ObjectMapper#writeValueAsString // 序列化bean对象为字符串
2. 二次反序列化绕过技术
2.1 SignedObject类介绍
java.security.SignedObject是一个用于创建真实运行时对象的类,特点:
- 包含另一个Serializable对象
- 实现了二次序列化/反序列化机制
关键方法:
// 构造函数
public SignedObject(Serializable object, ...) {
this.content = serialize(object); // 内部进行序列化
}
// getObject方法
public Object getObject() throws... {
return deserialize(this.content); // 内部进行反序列化
}
2.2 二次反序列化原理
当存在黑名单过滤时:
- 构造一个恶意的SignedObject对象
- 黑名单只检查第一层反序列化
- SignedObject.getObject()触发第二层反序列化,绕过黑名单
3. Jackson调用getter方法分析
3.1 调用链分析
调用路径:
POJONode.toString()
→ BaseJsonNode.toString()
→ InternalNodeUtils.nodeToString()
→ ObjectMapper.writeValueAsString()
关键点:
writeValueAsString()会调用对象的所有getter方法- 可以利用任意getter方法触发恶意操作
3.2 典型利用链
常用触发方式:
- 通过
TemplatesImpl#getOutputProperties执行命令 - 其他能够通过getter触发恶意操作的类
4. 实战案例:DASCTF 2025
4.1 题目分析
限制条件:
- 反序列化长度限制
- 重写
resolveClass方法实现黑名单 - 黑名单包含:
BadAttributeValueExpException链- Spring AOP链相关类
目标:
- 删除
/a文件后访问/flag获取flag
4.2 绕过方案
方案1:传统二次反序列化
- 使用
EventListenerList触发toString - 通过
createObjWithoutConstructor减少字节:- 实例化对象时所有属性为null
- 通过反射设置必要属性
- 使用
InflaterInputStream压缩payload - 删除恶意类调试信息进一步缩减
问题:最终长度仍超过限制(约1300字节)
方案2:优化利用Jackson特性
关键发现:
writeValueAsString会序列化所有对象并调用getter- 不需要依赖
SignedObject#getObject触发恶意readObject
优化链:
SignedObject#getObject返回TemplatesImpl类- Jackson序列化时自动调用
TemplatesImpl#getOutputProperties - 触发命令执行
优势:
- 最终payload仅1272字节
- 满足题目长度限制
4.3 完整利用步骤
- 构造恶意
TemplatesImpl对象 - 将其包装在
SignedObject中 - 构建触发toString的调用链
- 压缩payload
- 发送payload触发反序列化
- 删除
/a文件 - 访问
/flag获取flag
5. 防御建议
针对Jackson反序列化攻击的防御措施:
- 实现严格的黑名单过滤
- 限制反序列化深度
- 禁用不必要的getter方法
- 使用最新版本的Jackson
- 对输入进行严格验证和过滤
6. 总结
Jackson二次反序列化的关键点:
- 利用
SignedObject绕过单层黑名单 - 通过Jackson自动调用getter的特性简化利用链
- 多种技术组合缩减payload长度
- 理解底层机制才能有效优化攻击链
这种技术在CTF和实际渗透中都有广泛应用价值,理解其原理对于攻防双方都至关重要。