java反序列化原理-Demo(二)
java反序列化原理-Demo(二)
0x00 测试代码以及运行结果
测试代码:
package test;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class ReflectionPlay implements Serializable{ public static void main(String[] args) throws Exception { new ReflectionPlay().run(); } public void run() throws Exception { byte[] ObjectBytes=serialize(getObject()); deserialize(ObjectBytes); } //在此方法中返回恶意对象 public Object getObject() { String command = "calc.exe"; Object firstObject = Runtime.class; ReflectionObject[] reflectionChains = { /* * Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[] {}).invoke(null); Class.forName("java.lang.Runtime") .getMethod("exec", String.class) .invoke(runtime,"calc.exe"); * * */ //调用 Runtime.class 的getMethod方法,寻找 getRuntime方法,得到一个Method对象(getRuntime方法) //等同于 Runtime.class.getMethod("getRuntime",new Class[]{String.class,Class[].class}) new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), //调用 Method 的 invoker 方法可以得到一个Runtime对象 // 等同于 method.invoke(null),静态方法不用传入对象 new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), //调用RunTime对象的exec方法,并将 command作为参数执行命令 new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command}) }; return new ReadObject(new ReflectionChains(firstObject, reflectionChains)); } /* * 序列化对象到byte数组 * */ public byte[] serialize(final Object obj) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream objOut = new ObjectOutputStream(out); objOut.writeObject(obj); return out.toByteArray(); } /* * 从byte数组中反序列化对象 * */ public Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException { ByteArrayInputStream in = new ByteArrayInputStream(serialized); ObjectInputStream objIn = new ObjectInputStream(in); return objIn.readObject(); } /* * 一个模拟拥有漏洞的类,主要提供的功能是根据自己的属性中的值来进行反射调用 * */ class ReflectionObject implements Serializable{ private String methodName; private Class[] paramTypes; private Object[] args; public ReflectionObject(String methodName, Class[] paramTypes, Object[] args) { this.methodName = methodName; this.paramTypes = paramTypes; this.args = args; } //根据 methodName, paramTypes 来寻找对象的方法,利用 args作为参数进行调用 public Object transform(Object input) throws Exception { Class inputClass = input.getClass(); return inputClass.getMethod(methodName, paramTypes).invoke(input, args); } } /* * 一个用来模拟提供恶意代码的类, * 主要的功能是将 ReflectionObject进行串联调用,与ReflectionObject一起构成漏洞代码的一部分 * */ class ReflectionChains implements Serializable{ private Object firstObject; private ReflectionObject[] reflectionObjects; public ReflectionChains(Object firstObject, ReflectionObject[] reflectionObjects) {//ReflectionChains构造方法 this.firstObject = firstObject; this.reflectionObjects = reflectionObjects; } public Object execute() throws Exception { Object concurrentObject = firstObject; for (ReflectionObject reflectionObject : reflectionObjects) { concurrentObject = reflectionObject.transform(concurrentObject); System.out.println(concurrentObject); } return concurrentObject; } } /** * 一个等待序列化的类,拥有一个属性和一个重写了的readObject方法 * 并且在readObject方法中执行了该属性的一个方法 * */ class ReadObject implements Serializable { private ReflectionChains reflectionChains; public ReadObject(ReflectionChains reflectionChains) { this.reflectionChains = reflectionChains; } //当反序列化的时候,这个代码会被调用 //该方法被调用的时候其属性都是空 private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { try { //用来模拟当readObject的时候,对自身的属性进行了一些额外的操作 reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null); reflectionChains.execute(); } catch (Exception e) { e.printStackTrace(); } } } }
运行结果:
0x01 测试代码分析
涉及到的类:
类名 | 是否继承 Serializable | 方法名 |
---|---|---|
ReflectionObject | 继承 | transform |
ReflectionChains | 继承 | execute |
ReadObject | 继承 | readObject(重写) |
注:要想产生反序列漏洞,需要重写readObjec方法
代码流程:
- serialize(getObject());将一个对象序列化
getObject(); 生成一个ReflectionObject数组,包含三个ReflectionObject对象;然后使用ReflectionObject数组生成一个ReflectionChains对象,继续使用ReflectionChains对象生成一个ReadObject对象
new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), //调用 Method 的 invoker 方法可以得到一个Runtime对象 // 等同于 method.invoke(null),静态方法不用传入对象new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), //调用RunTime对象的exec方法,并将 command作为参数执行命令new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})}; return new ReadObject(new ReflectionChains(firstObject, reflectionChains));
- deserialize(ObjectBytes);将序列化后的字节反序列化
将传入的字节进行反序列化,因为传入的字节是一个ReadObjec对象序列化后的,因此在反序列化时需要调用ReadObjec的readObjec方法(该方法已经被重写)private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException { try { //用来模拟当readObject的时候,对自身的属性进行了一些额外的操作 reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null); reflectionChains.execute(); } catch (Exception e) { e.printStackTrace(); } }
在这里我们需要注重看
在生成ReadObject对象时传入的参数是ReflectionChains对象,所以这里调用的execute()方法就是ReflectionChains的execute()方法。
接着分析ReflectionChains的execute()方法:public Object execute() throws Exception { Object concurrentObject = firstObject; for (ReflectionObject reflectionObject : reflectionObjects) { concurrentObject = reflectionObject.transform(concurrentObject); System.out.println(concurrentObject); } return concurrentObject; }
关键代码:
由上面可知生成ReflectionChains对象时传入的参数是ReflectionObject数组,这段代码的含义便是:遍历ReflectionObject数组,每一个元素(ReflectionObject)执行transform方法
继续分析ReflectionObject的transform方法:public Object transform(Object input) throws Exception { Class inputClass = input.getClass(); 得到input对象是那个类的类名 return inputClass.getMethod(methodName, paramTypes).invoke(input, args); 通过类名调用该类的某一方法 }
注意:
通过以上分析可以了解到,进行反序列化的反射链为:
deserialize(ObjectBytes); 调用ReadObject类的readObject方法;接下来调用ReflectionChains类的execute方法;接下来通过遍历ReflectionObject数组调用ReflectionObject类的transform方法
0x02 核心分析
通过以上代码分析看,最终需要执行的便是以下代码:
new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), //调用 Method 的 invoker 方法可以得到一个Runtime对象 // 等同于 method.invoke(null),静态方法不用传入对象new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), //调用RunTime对象的exec方法,并将 command作为参数执行命令 new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), 等同于执行了:Runtime.class.getMethod("getRuntime",new Class[]{String.class,Class[].class})new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),等同于执行了:method.invoke(null),静态方法不用传入对象new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})RunTime对象的exec方法,并将 command作为参数执行命令
第一个对象执行完成:
Runtime.class.getMethod("getRuntime", new Class[0]);
输出的结果:java.lang.Runtime.getRuntime() 输出的是一个类
第二个对象执行完成:
Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]);
输出结果:java.lang.Runtime@74a14482 输出的是一个Runtime的对象
第二个对象执行完成:
Runtime t = (Runtime) Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]);
t.exec("calc.exe")
0x03补充
package test;import java.lang.reflect.InvocationTargetException;public class test5 { public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { //Class inputClass = input.getClass(); //inputClass.getMethod(methodName, paramTypes).invoke(input, args) //new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}) Class inputClass1 = Runtime.class.getClass(); Object concurrentObject1 = inputClass1.getMethod("getMethod",new Class[]{String.class, Class[].class}).invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]}); System.out.println(inputClass1.getMethod("getMethod",new Class[]{String.class, Class[].class}).invoke(Runtime.class, new Object[]{"getRuntime", new Class[0]})); //public static java.lang.Runtime java.lang.Runtime.getRuntime() //new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}) Class inputClass2 = concurrentObject1.getClass(); Object concurrentObject2 = inputClass2.getMethod("invoke",new Class[]{Object.class, Object[].class}).invoke(concurrentObject1, new Object[]{null, new Object[0]}); System.out.println(concurrentObject2); //java.lang.Runtime@3d4eac69 //new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command}) Class inputClass3 = concurrentObject2.getClass(); Object concurrentObject3 = inputClass3.getMethod("exec",new Class[]{String.class}).invoke(concurrentObject2,new Object[]{"calc.exe"}); System.out.println(concurrentObject3); /* * 对比user类: * inputClass.getMethod(methodName, paramTypes).invoke(input, args) * 对参数说明: * inputClass user.getClass() * methodName 调用的方法名称 * paramTypes 调用方法的参数类型 * input 是 inputClass的一个对象 * args 调用方法的参数 * * 函数返回结果: * input对象调用methodName的方法(传入的参数是args)返回的结果:比如 void setName(String name){} 返回的是null * */ }}
参考链接:http://www.freebuf.com/vuls/170344.html