Spring-AOP下toString新触发
字数 1551 2025-08-30 06:50:35

Spring-AOP下toString新触发机制分析与利用

前言

在某个CTF线下赛的Java反序列化题目中,出现了一种刁钻的黑名单绕过技术。传统的Jackson调用getter攻击TemplatesImpl的方法被限制,特别是toString的入口几乎都被禁止。本文将详细分析如何利用JdkDynamicAopProxy实现toString的新触发方式。

JdkDynamicAopProxy机制分析

基本工作原理

JdkDynamicAopProxy类最初用于解决Jackson链的不稳定触发问题。其核心机制包括:

  1. 实现了InvocationHandler接口,通过JDK动态代理实现AOP功能
  2. 接收一个AdvisedSupport对象作为advised属性
  3. AdvisedSupport对象包含targetSource属性,存储了委托代理的目标对象
  4. 通过TargetSource#getTarget方法获取目标对象

方法调用流程

当代理对象调用方法时,流程如下:

  1. 调用请求被转交给调用处理器的invoke方法
  2. 如果目标对象的接口没有定义equalshashCode方法,则直接调用JdkDynamicAopProxy自定义的实现
  3. 如果AOP配置对象(advised)中没有为当前调用的方法设置方法拦截器(chain为空),则直接调用目标对象实现的方法

关键反射调用

invokeJoinpointUsingReflection方法使用反射调用目标方法,需要三个要素:

  • 接收对象(target)
  • 方法(Method)
  • 参数数组(args)

当抛出IllegalArgumentException异常时,系统会对target进行字符串拼接,从而隐式触发target的toString方法。

异常触发机制

异常触发toString的原理

这种在报错后通过对象拼接字符串来打印异常信息的操作并非首次出现,类似的案例包括:

  • Dubbo的CVE-2021-43297漏洞,在异常处理时进行反序列化并将对象进行字符串拼接

触发IllegalArgumentException的条件

要触发该异常,需要满足以下条件:

  1. 只有target参数相对容易控制
  2. method和args参数在外部方法调用中相对固定
  3. 需要目标对象没有实现代理接口中声明的方法

JDK动态代理特点利用

利用JDK动态代理的特点:

  • 创建代理对象Proxy时传递类加载器、接口数组和调用处理器
  • target位于调用处理器中
  • InvocationHandler不会检查target是否实现了接口定义的方法

因此,只要委托代理的对象没有实现代理接口中声明的方法,调用时就会抛出异常。

实际利用验证

简单验证示例

// 对1这个对象进行调用Runtime的exec方法
// 会报错:java.lang.IllegalArgumentException: object is not an instance of declaring class

可利用的类

java.util.Collections$SetFromMap是一个可利用的类:

  1. 构造函数传入一个空的Map
  2. 通过反射修改m为Proxy代理对象
  3. 在readObject时调用m.keySet触发JdkDynamicAopProxy的invoke方法

POC编写示例

以Jackson攻击TemplatesImpl为例:

// 创建AdvisedSupport对象
AdvisedSupport advisedSupport = new AdvisedSupport();
// 设置目标源
TargetSource targetSource = new SingletonTargetSource(templatesImpl);
advisedSupport.setTargetSource(targetSource);

// 创建代理对象
InvocationHandler handler = new JdkDynamicAopProxy(advisedSupport);
Object proxy = Proxy.newProxyInstance(
    handler.getClass().getClassLoader(),
    new Class[]{Map.class}, // 使用Map接口
    handler
);

// 创建SetFromMap对象
Map map = Collections.singletonMap("key", "value");
Set set = Collections.newSetFromMap(map);

// 通过反射修改SetFromMap的m字段为代理对象
Field mField = set.getClass().getDeclaredField("m");
mField.setAccessible(true);
mField.set(set, proxy);

// 序列化set对象
// ...

防御建议

  1. 严格限制反序列化的类白名单
  2. 检查并过滤包含AOP相关类的序列化数据
  3. 更新相关框架到最新版本,应用安全补丁
  4. 监控和记录异常情况,特别是IllegalArgumentException的频繁出现

总结

通过分析JdkDynamicAopProxy的工作原理和异常处理机制,我们发现了一种新的toString触发方式。这种技术可以绕过传统的黑名单限制,为Java反序列化漏洞利用提供了新的思路。安全开发人员应了解这种攻击模式,并采取相应的防御措施。

Spring-AOP下toString新触发机制分析与利用 前言 在某个CTF线下赛的Java反序列化题目中,出现了一种刁钻的黑名单绕过技术。传统的Jackson调用getter攻击TemplatesImpl的方法被限制,特别是toString的入口几乎都被禁止。本文将详细分析如何利用JdkDynamicAopProxy实现toString的新触发方式。 JdkDynamicAopProxy机制分析 基本工作原理 JdkDynamicAopProxy类最初用于解决Jackson链的不稳定触发问题。其核心机制包括: 实现了 InvocationHandler 接口,通过JDK动态代理实现AOP功能 接收一个 AdvisedSupport 对象作为 advised 属性 AdvisedSupport 对象包含 targetSource 属性,存储了委托代理的目标对象 通过 TargetSource#getTarget 方法获取目标对象 方法调用流程 当代理对象调用方法时,流程如下: 调用请求被转交给调用处理器的 invoke 方法 如果目标对象的接口没有定义 equals 或 hashCode 方法,则直接调用JdkDynamicAopProxy自定义的实现 如果AOP配置对象( advised )中没有为当前调用的方法设置方法拦截器(chain为空),则直接调用目标对象实现的方法 关键反射调用 invokeJoinpointUsingReflection 方法使用反射调用目标方法,需要三个要素: 接收对象(target) 方法(Method) 参数数组(args) 当抛出 IllegalArgumentException 异常时,系统会对target进行字符串拼接,从而隐式触发target的 toString 方法。 异常触发机制 异常触发toString的原理 这种在报错后通过对象拼接字符串来打印异常信息的操作并非首次出现,类似的案例包括: Dubbo的CVE-2021-43297漏洞,在异常处理时进行反序列化并将对象进行字符串拼接 触发IllegalArgumentException的条件 要触发该异常,需要满足以下条件: 只有target参数相对容易控制 method和args参数在外部方法调用中相对固定 需要目标对象没有实现代理接口中声明的方法 JDK动态代理特点利用 利用JDK动态代理的特点: 创建代理对象Proxy时传递类加载器、接口数组和调用处理器 target位于调用处理器中 InvocationHandler不会检查target是否实现了接口定义的方法 因此,只要委托代理的对象没有实现代理接口中声明的方法,调用时就会抛出异常。 实际利用验证 简单验证示例 可利用的类 java.util.Collections$SetFromMap 是一个可利用的类: 构造函数传入一个空的Map 通过反射修改m为Proxy代理对象 在readObject时调用m.keySet触发JdkDynamicAopProxy的invoke方法 POC编写示例 以Jackson攻击TemplatesImpl为例: 防御建议 严格限制反序列化的类白名单 检查并过滤包含AOP相关类的序列化数据 更新相关框架到最新版本,应用安全补丁 监控和记录异常情况,特别是IllegalArgumentException的频繁出现 总结 通过分析JdkDynamicAopProxy的工作原理和异常处理机制,我们发现了一种新的toString触发方式。这种技术可以绕过传统的黑名单限制,为Java反序列化漏洞利用提供了新的思路。安全开发人员应了解这种攻击模式,并采取相应的防御措施。