天天看點

Java 代碼審計 — 2. Reflection

參考: https://zhishihezi.net/b/5d644b6f81cbc9e40460fe7eea3c7925 https://stackoverflow.com/questions/16966629/what-is-the-difference-between-getfields-and-getdeclaredfields-in-java-reflectio

反射機制是 java 語言的動态性的重要展現,也是 java 的各種架構底層實作的靈魂。通過反射我們可以:

擷取到任何類的成員方法 (<code>Methods</code>)、成員變量 (<code>Fields</code>)、構造方法 (<code>Constructors</code>) 等資訊。

動态建立 java 類執行個體、調用任意的類方法、修改任意的類成員變量值等。

總而言之,程式在運作時的行為是固定的,如果想在運作時改變,就需要用到反射技術。

java 反射在編寫漏洞利用代碼、代碼審計、繞過 RASP 方法限制等中起到了至關重要的作用。

假想一個場景,如果我們需要根據使用者輸入來動态的建立類對象。可能會想到這樣的代碼。

但這個操作是不行的,java 靜态編譯特性決定了編譯無法通過。而借助反射機制可以完成這個目的。

我們以 <code>java.lang.Runtime</code> 為例,因為它有一個 exec 方法可以執行系統指令,是以在很多 exp 中都能看到通過反射調用它來 rce 。這塊我們就嘗試通過它來執行系統指令。

在進入代碼之前,介紹下基本步驟:

擷取目标類 Class 對象,以便擷取目标類的構造方法。

擷取目标類構造方法,以便建立目标執行個體。

因為 Runtime 構造方法是 private 的,無法直接調用,是以需要擷取到修改一下通路權限。

建立目标執行個體,以便調用執行類中的方法。

擷取目标類中要執行的方法,并調用執行該方法。

擷取執行輸出。

java 反射操作的是 <code>java.lang.Class</code> 對象,是以我們需要先想辦法擷取到這個對象,通常我們有如下幾種方式擷取一個類的 Class 對象,以 <code>java.lang.Runtime</code> 為例:

幾個擷取 Class 的方式有些差別,涉及是否初始化目标類的問題,詳見文章末尾。

如果需要反射内部類,則有 特殊的文法

因為我們最終要執行 exec 函數是需要一個對象執行個體的,是以我們需要建立一個對象執行個體,并且由于 Runtime 的構造方法是私有的,是以我們需要使用 constructor 對象來修改通路權限。

Java 代碼審計 — 2. Reflection

從 Runtime 類代碼注釋,可以看到它本身是不希望除了其自身以外的任何人去建立該類執行個體,是以我們沒辦法 <code>new</code> 一個 Runtime 類執行個體。我們可以借助反射機制,修改方法通路權限進而間接的建立出了 Runtime 對象。

下面是 Class 對象擷取構造方法的相關函數。

Java 代碼審計 — 2. Reflection

<code>getConstructor</code> 和 <code>getDeclaredConstructor</code>

前者隻能擷取到公有的構造方法,而後者可以擷取到所有構造方法。

擷取到 <code>Constructor</code> 以後我們可以通過 <code>constructor.newInstance()</code> 來建立類執行個體。

Java 代碼審計 — 2. Reflection

若無通路權限,則可以使用 <code>constructor.setAccessible(true)</code> 進行修改。

為了執行 exec 這個方法,我們需要擷取到這個方法。

下面是 Class 對象擷取方法的相關函數。

Java 代碼審計 — 2. Reflection

<code>getMethod</code> 和 <code>getDeclaredMethod</code>

前者會傳回目前類公有方法和繼承的公有方法,而後者會傳回目前類所有方法。

擷取到 <code>java.lang.reflect.Method</code> 對象後,我們可以通過其 <code>invoke</code> 方法來調用該方法。

Java 代碼審計 — 2. Reflection

如果調用的是 static 方法,則執行個體對象需要傳入 <code>null</code>

若無調用權限,則可以使用 <code>method.setAccessible(true)</code> 進行修改

Java 反射不但可以擷取類所有的成員變量名稱,還可以無視權限修飾符實作修改對應的值。

Java 代碼審計 — 2. Reflection

<code>getField</code> 和 <code>getDeclaredField</code>

前者會傳回目前類公有字段和繼承的公有字段,而後者會傳回目前類所有字段。

若無修改權限,則可以使用 <code>field.setAccessible(true)</code> 進行修改。

若修改 final 屬性的變量,則需要 特殊的文法

以下順序的代碼塊,哪個會先執行呢?

關于類中的代碼段

static{} 是在類初始化時調用的。

{} 代碼會放在構造函數中 super() 後,但目前構造函數内容前。

關于幾種擷取 Class 對象方法

forName 第二個參數就是控制是否執行類的初始化,預設為 true。

Java 代碼審計 — 2. Reflection
上一篇: XSLT
下一篇: XPATH Injection