原生新链-SPring AOP 原生链挖掘思路分析
字数 2304 2025-08-30 06:50:36

Spring AOP 原生链挖掘思路分析

前言

本文详细分析Spring AOP原生链的挖掘思路,该链在Java安全攻防赛中表现出色,能够绕过多种类过滤限制。我们将从发现者视角完整剖析这条链的挖掘过程。

Sink点分析:AbstractAspectJAdvice

关键方法分析

AbstractAspectJAdvice类中,我们发现以下关键方法:

public Object invoke(MethodInvocation mi) throws Throwable {
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    return invokeAdviceMethod(pmi, null, null);
}

以及:

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterCount() == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        return this.aspectJAdviceMethod.invoke(
            this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
            this.aspectJAdviceMethod + "]; pointcut expression [" +
            this.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }
}

利用条件

要实现利用,需要控制以下关键点:

  1. aspectJAdviceMethod:需要能够控制要调用的方法
  2. aspectInstanceFactory.getAspectInstance():需要能够控制方法调用的目标对象

AspectInstanceFactory分析

AspectInstanceFactory有以下实现类:

  • SingletonAspectInstanceFactory:直接返回aspectInstance对象
  • SimpleAspectInstanceFactory:通过反射创建实例
  • PrototypeAspectInstanceFactory:每次调用都创建新实例

其中SingletonAspectInstanceFactorygetAspectInstance方法直接返回对象:

public Object getAspectInstance() {
    return this.aspectInstance;
}

我们可以通过反射控制这个aspectInstance,测试证明可以使用TemplatesImpl类成功实现任意代码执行。

利用链构建

ReflectiveMethodInvocation分析

我们需要向上寻找调用链,发现ReflectiveMethodInvocation类的proceed方法:

public Object proceed() throws Throwable {
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            return proceed();
        }
    }
    else {
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

要利用这个方法,需要控制interceptorOrInterceptionAdvice对象为AspectJAroundAdvice

控制interceptorsAndDynamicMethodMatchers

interceptorsAndDynamicMethodMatchers是从List中获取的,但ReflectiveMethodInvocation没有实现Serializable接口,无法直接序列化控制。

解决方案是寻找动态创建ReflectiveMethodInvocation对象的地方,且该创建者类需要实现Serializable接口。

JdkDynamicAopProxy

JdkDynamicAopProxy类满足上述条件:

  1. 实现了Serializable接口
  2. invoke方法中实例化ReflectiveMethodInvocation对象
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // ...
    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    // ...
    retVal = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain).proceed();
    // ...
}

控制chain

chain是通过getInterceptorsAndDynamicInterceptionAdvice方法获取的,该方法有两种获取方式:

  1. methodCache中直接获取
  2. 从工厂类重新创建

我们需要关注第一种方式,通过控制AdvisedSupport对象来影响chain的生成。

getInterceptors方法分析

getInterceptors方法最终从advice获取拦截器:

public static MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    for (AdvisorAdapter adapter : ADAPTERS) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[0]);
}

要利用这个方法,需要传入的对象同时实现AdviceMethodInterceptor接口。

突破Advice限制

虽然AspectJAroundAdvice实现了MethodInterceptor接口但没有实现Advice接口,我们可以使用JdkDynamicAopProxy代理这两个接口,设置target为AspectJAroundAdvice

Source点分析

要触发整个利用链,需要找到一个readObject方法中调用任意对象方法的点。BadAttributeValueExpException类完美满足这个需求:

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ObjectInputStream.GetField gf = ois.readFields();
    Object valObj = gf.get("val", null);
    
    if (valObj == null) {
        val = null;
    } else if (valObj instanceof String) {
        val= valObj;
    } else if (System.getSecurityManager() == null
            || valObj instanceof Long
            || valObj instanceof Integer
            || valObj instanceof Float
            || valObj instanceof Double
            || valObj instanceof Byte
            || valObj instanceof Short
            || valObj instanceof Boolean) {
        val = valObj.toString();
    } else { // the serialized object is from a version without JDK-8019292 fix
        val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
    }
}

通过控制valObj触发其toString方法,进而触发代理类的invoke方法,完成整个利用链的闭环。

完整利用链

  1. BadAttributeValueExpException.readObject() 触发 toString()
  2. 代理对象 toString() 触发 JdkDynamicAopProxy.invoke()
  3. JdkDynamicAopProxy.invoke() 创建并调用 ReflectiveMethodInvocation.proceed()
  4. ReflectiveMethodInvocation.proceed() 调用 AspectJAroundAdvice.invoke()
  5. AspectJAroundAdvice.invoke() 调用 AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs()
  6. AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs() 反射调用任意方法

总结

这条Spring AOP原生链的挖掘过程展示了从sink点向上寻找调用链的完整思路,通过分析各个关键类的交互关系和接口实现情况,最终构建出一条完整的利用链。该链的特别之处在于:

  1. 利用了Spring AOP的核心机制
  2. 能够绕过常见的类过滤限制
  3. 结合了动态代理和反射机制
  4. 通过BadAttributeValueExpException触发,兼容性高

这种挖掘思路可以应用于其他框架的链挖掘中,重点关注框架核心组件的交互方式和接口实现情况。

Spring AOP 原生链挖掘思路分析 前言 本文详细分析Spring AOP原生链的挖掘思路,该链在Java安全攻防赛中表现出色,能够绕过多种类过滤限制。我们将从发现者视角完整剖析这条链的挖掘过程。 Sink点分析:AbstractAspectJAdvice 关键方法分析 在 AbstractAspectJAdvice 类中,我们发现以下关键方法: 以及: 利用条件 要实现利用,需要控制以下关键点: aspectJAdviceMethod :需要能够控制要调用的方法 aspectInstanceFactory.getAspectInstance() :需要能够控制方法调用的目标对象 AspectInstanceFactory分析 AspectInstanceFactory 有以下实现类: SingletonAspectInstanceFactory :直接返回aspectInstance对象 SimpleAspectInstanceFactory :通过反射创建实例 PrototypeAspectInstanceFactory :每次调用都创建新实例 其中 SingletonAspectInstanceFactory 的 getAspectInstance 方法直接返回对象: 我们可以通过反射控制这个aspectInstance,测试证明可以使用 TemplatesImpl 类成功实现任意代码执行。 利用链构建 ReflectiveMethodInvocation分析 我们需要向上寻找调用链,发现 ReflectiveMethodInvocation 类的 proceed 方法: 要利用这个方法,需要控制 interceptorOrInterceptionAdvice 对象为 AspectJAroundAdvice 。 控制interceptorsAndDynamicMethodMatchers interceptorsAndDynamicMethodMatchers 是从 List 中获取的,但 ReflectiveMethodInvocation 没有实现 Serializable 接口,无法直接序列化控制。 解决方案是寻找动态创建 ReflectiveMethodInvocation 对象的地方,且该创建者类需要实现 Serializable 接口。 JdkDynamicAopProxy JdkDynamicAopProxy 类满足上述条件: 实现了 Serializable 接口 在 invoke 方法中实例化 ReflectiveMethodInvocation 对象 控制chain chain 是通过 getInterceptorsAndDynamicInterceptionAdvice 方法获取的,该方法有两种获取方式: 从 methodCache 中直接获取 从工厂类重新创建 我们需要关注第一种方式,通过控制 AdvisedSupport 对象来影响chain的生成。 getInterceptors方法分析 getInterceptors 方法最终从 advice 获取拦截器: 要利用这个方法,需要传入的对象同时实现 Advice 和 MethodInterceptor 接口。 突破Advice限制 虽然 AspectJAroundAdvice 实现了 MethodInterceptor 接口但没有实现 Advice 接口,我们可以使用 JdkDynamicAopProxy 代理这两个接口,设置target为 AspectJAroundAdvice 。 Source点分析 要触发整个利用链,需要找到一个 readObject 方法中调用任意对象方法的点。 BadAttributeValueExpException 类完美满足这个需求: 通过控制 valObj 触发其 toString 方法,进而触发代理类的 invoke 方法,完成整个利用链的闭环。 完整利用链 BadAttributeValueExpException.readObject() 触发 toString() 代理对象 toString() 触发 JdkDynamicAopProxy.invoke() JdkDynamicAopProxy.invoke() 创建并调用 ReflectiveMethodInvocation.proceed() ReflectiveMethodInvocation.proceed() 调用 AspectJAroundAdvice.invoke() AspectJAroundAdvice.invoke() 调用 AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs() AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs() 反射调用任意方法 总结 这条Spring AOP原生链的挖掘过程展示了从sink点向上寻找调用链的完整思路,通过分析各个关键类的交互关系和接口实现情况,最终构建出一条完整的利用链。该链的特别之处在于: 利用了Spring AOP的核心机制 能够绕过常见的类过滤限制 结合了动态代理和反射机制 通过 BadAttributeValueExpException 触发,兼容性高 这种挖掘思路可以应用于其他框架的链挖掘中,重点关注框架核心组件的交互方式和接口实现情况。