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 二次反序列化原理

当存在黑名单过滤时:

  1. 构造一个恶意的SignedObject对象
  2. 黑名单只检查第一层反序列化
  3. SignedObject.getObject()触发第二层反序列化,绕过黑名单

3. Jackson调用getter方法分析

3.1 调用链分析

调用路径:

POJONode.toString() 
→ BaseJsonNode.toString() 
→ InternalNodeUtils.nodeToString() 
→ ObjectMapper.writeValueAsString()

关键点:

  • writeValueAsString()会调用对象的所有getter方法
  • 可以利用任意getter方法触发恶意操作

3.2 典型利用链

常用触发方式:

  1. 通过TemplatesImpl#getOutputProperties执行命令
  2. 其他能够通过getter触发恶意操作的类

4. 实战案例:DASCTF 2025

4.1 题目分析

限制条件:

  1. 反序列化长度限制
  2. 重写resolveClass方法实现黑名单
  3. 黑名单包含:
    • BadAttributeValueExpException
    • Spring AOP链相关类

目标:

  • 删除/a文件后访问/flag获取flag

4.2 绕过方案

方案1:传统二次反序列化

  1. 使用EventListenerList触发toString
  2. 通过createObjWithoutConstructor减少字节:
    • 实例化对象时所有属性为null
    • 通过反射设置必要属性
  3. 使用InflaterInputStream压缩payload
  4. 删除恶意类调试信息进一步缩减

问题:最终长度仍超过限制(约1300字节)

方案2:优化利用Jackson特性

关键发现:

  • writeValueAsString会序列化所有对象并调用getter
  • 不需要依赖SignedObject#getObject触发恶意readObject

优化链:

  1. SignedObject#getObject返回TemplatesImpl
  2. Jackson序列化时自动调用TemplatesImpl#getOutputProperties
  3. 触发命令执行

优势:

  • 最终payload仅1272字节
  • 满足题目长度限制

4.3 完整利用步骤

  1. 构造恶意TemplatesImpl对象
  2. 将其包装在SignedObject
  3. 构建触发toString的调用链
  4. 压缩payload
  5. 发送payload触发反序列化
  6. 删除/a文件
  7. 访问/flag获取flag

5. 防御建议

针对Jackson反序列化攻击的防御措施:

  1. 实现严格的黑名单过滤
  2. 限制反序列化深度
  3. 禁用不必要的getter方法
  4. 使用最新版本的Jackson
  5. 对输入进行严格验证和过滤

6. 总结

Jackson二次反序列化的关键点:

  1. 利用SignedObject绕过单层黑名单
  2. 通过Jackson自动调用getter的特性简化利用链
  3. 多种技术组合缩减payload长度
  4. 理解底层机制才能有效优化攻击链

这种技术在CTF和实际渗透中都有广泛应用价值,理解其原理对于攻防双方都至关重要。

Jackson二次反序列化分析与利用 1. Jackson反序列化基础 1.1 Jackson反序列化特点 Jackson在CTF比赛中被广泛使用的原因: Spring Boot框架自带Jackson依赖 反序列化过程没有内置黑名单限制 可以调用任意getter方法 1.2 基本利用方式 常用方法: 2. 二次反序列化绕过技术 2.1 SignedObject类介绍 java.security.SignedObject 是一个用于创建真实运行时对象的类,特点: 包含另一个Serializable对象 实现了二次序列化/反序列化机制 关键方法: 2.2 二次反序列化原理 当存在黑名单过滤时: 构造一个恶意的SignedObject对象 黑名单只检查第一层反序列化 SignedObject.getObject()触发第二层反序列化,绕过黑名单 3. Jackson调用getter方法分析 3.1 调用链分析 调用路径: 关键点: 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和实际渗透中都有广泛应用价值,理解其原理对于攻防双方都至关重要。