站長資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        java反序列化引發(fā)的遠(yuǎn)程代碼執(zhí)行漏洞原理分析

        java反序列化引發(fā)的遠(yuǎn)程代碼執(zhí)行漏洞原理分析

        主要有3個部分組成:

        1、Java的反省機(jī)制

        2、Java的序列化處理

        3、Java的遠(yuǎn)程代碼執(zhí)行

        Java的反射與代碼執(zhí)行

        我們先看個簡單的例子,使用Java調(diào)用計算器程序:

        import java.io.IOException; import java.lang.Runtime; public class Test {     public static void main(String[] args) {         Runtime env = Runtime.getRuntime();         String cmd = "calc.exe";             try {             env.exec(cmd);         } catch (IOException e) {             e.printStackTrace();         }     } }

        我們從java.lang包中導(dǎo)入Runtime類,之后調(diào)用其getRuntime方法得到1個Runtime對象,該對象可以用于JVM虛擬機(jī)運(yùn)行狀態(tài)的處理。接著我們調(diào)用其exec方法,傳入1個字符串作為參數(shù)。

        此時,將啟動本地計算機(jī)上的計算器程序。

        下面我們通過Java的反省機(jī)制對上述的代碼進(jìn)行重寫。通過Java的反省機(jī)制可以動態(tài)的調(diào)用代碼,而逃過一些服務(wù)端黑名單的處理:

        import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;  public class Test {      public static void main(String[] args) {         try {             Class<?> cls = Class.forName("java.lang.Runtime");                         String cmd = "calc.exe";             try {                 Method getRuntime = cls.getMethod("getRuntime", new Class[] {});                                 Object runtime = getRuntime.invoke(null);                 Method exec = cls.getMethod("exec", String.class);                 exec.invoke(runtime, cmd);             } catch (NoSuchMethodException e) {                 e.printStackTrace();             } catch (SecurityException e) {                 e.printStackTrace();             } catch (IllegalAccessException e) {                 e.printStackTrace();             } catch (IllegalArgumentException e) {                 e.printStackTrace();             } catch (InvocationTargetException e) {                 e.printStackTrace();             }         } catch (ClassNotFoundException e1) {             e1.printStackTrace();         }     } }

        上述代碼看起來很繁瑣,實(shí)際上并不是很難。首先,通過Class.forName傳入1個字符串作為參數(shù),其返回1個Class的實(shí)例。而其作用是根據(jù)對應(yīng)的名稱找到對應(yīng)的類。

        接著我們使用Class實(shí)例的getMethod方法獲取對應(yīng)類的getRuntime方法,由于該類沒有參數(shù),因此可以將其設(shè)置為null或使用匿名類來處理。

        Method getRuntime = cls.getMethod("getRuntime", new Class[] {});

        之后通過得到的方法的實(shí)例的invoke方法調(diào)用對應(yīng)的類方法,由于沒有參數(shù)則傳入null即可。同理,我們再獲取到exec方法。

        Java序列化處理

        對于Java中的序列化處理,對應(yīng)的類需要實(shí)現(xiàn)Serializable接口,例如:

        import java.io.Serializable; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class Reader implements Serializable {     private static final long serialVersionUID = 10L;         private void readObject(ObjectInputStream stream) {         System.out.println("foo...bar...");     }    public static byte[] serialize(Object obj) {        //序列化對象         ByteArrayOutputStream out = new ByteArrayOutputStream();         ObjectOutputStream output = null;             try {             output = new ObjectOutputStream(out);             output.writeObject(obj);             output.flush();             output.close();          } catch (IOException e) {             e.printStackTrace();         }        return out.toByteArray();      }    public static Object deserialize(byte[] bytes) {        //反序列化處理         ByteArrayInputStream in = new ByteArrayInputStream(bytes);         ObjectInputStream input;         Object obj = null;             try {             input = new ObjectInputStream(in);             obj = input.readObject();         } catch (IOException e) {             e.printStackTrace();         } catch (ClassNotFoundException e) {             e.printStackTrace();         }        return obj;      }         public static void main(String[] args) {             byte[] data = serialize(new Reader()); //對類自身進(jìn)行序列化         Object response = deserialize(data);         System.out.println(response);     } }

        在這里我們重寫了該類的readObject方法,用于讀取對象用于測試。其中比較重要的2個函數(shù)是serialize和deserialize,分別用于序列化和反序列化處理。

        其中,serialize方法需要傳入1個對象作為參數(shù),其輸出結(jié)果為1個字節(jié)數(shù)組。在該類中,其中的對象輸出流ObjectOutputStream主要用于ByteArrayOutputStream進(jìn)行包裝,之后使用其writeObject方法將對象寫入進(jìn)去,最后我們通過ByteArrayOutputStream實(shí)例的toByteArray方法得到字節(jié)數(shù)組。

        而在deserialize方法中,需要傳入1個字節(jié)數(shù)組,而返回值為1個Object對象。與之前的序列化serialize函數(shù)類似,此時我們使用ByteArrayInputStream接收字節(jié)數(shù)組,之后使用ObjectInputStream對ByteArrayInputStream進(jìn)行包裝,接著調(diào)用其readObject方法得到1個Object對象,并將其返回。

        當(dāng)我們運(yùn)行該類時,將得到如下的結(jié)果:

        java反序列化引發(fā)的遠(yuǎn)程代碼執(zhí)行漏洞原理分析

        Java遠(yuǎn)程通信與傳輸

        為了實(shí)現(xiàn)Java代碼的遠(yuǎn)程傳輸及遠(yuǎn)程代碼執(zhí)行,我們可以借助RMI、RPC等方式。而在這里我們使用Socket進(jìn)行服務(wù)端及客戶端處理。

        首先是服務(wù)器端,監(jiān)聽本地的8888端口,其代碼為:

        import java.net.Socket; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; public class Server {     public static void main(String[] args) throws ClassNotFoundException {             int port = 8888;             try {             ServerSocket server = new ServerSocket(port);             System.out.println("Server is waiting for connect");             Socket socket = server.accept();             InputStream input = socket.getInputStream();                         byte[] bytes = new byte[1024];             int length = 0;                         while((length=input.read(bytes))!=-1) {                 String out = new String(bytes, 0, length, "UTF-8");                 System.out.println(out);             }             input.close();             socket.close();             server.close();         } catch (IOException e) {             e.printStackTrace();         }     } }

        我們通過傳入1個端口來實(shí)例化ServerSocket類,此時得到1個服務(wù)器的socket,之后調(diào)用其accept方法接收客戶端的請求。此時,得到了1個socket對象,而通過socket對象的getInputStream方法獲取輸入流,并指定1個長度為1024的字節(jié)數(shù)組。

        接著調(diào)用socket的read方法讀取那么指定長度的字節(jié)序列,之后通過String構(gòu)造器將字節(jié)數(shù)組轉(zhuǎn)換為字符串并輸出。這樣我們就得到了客戶端傳輸?shù)膬?nèi)容。

        而對于客戶端器,其代碼類似如下:

        import java.io.IOException; import java.net.Socket; import java.io.OutputStream; public class Client {     public static void main(String[] args) {         String host = "192.168.1.108";                 int port = 8888;         try {             Socket socket = new Socket(host, port);             OutputStream output = socket.getOutputStream();             String message = "Hello,Java Socket Server";             output.write(message.getBytes("UTF-8"));             output.close();             socket.close();         } catch (IOException e) {             e.printStackTrace();         }     } }

        在客戶端,我們通過Socket對象傳遞要連接的IP地址和端口,之后通過socket對象的getOutputStream方法獲取到輸出流,用于往服務(wù)器端發(fā)送輸出。由于這里只是演示,使用的是本地的主機(jī)IP。而在實(shí)際應(yīng)用中,如果我們知道某個外網(wǎng)主機(jī)的IP及開放的端口,如果當(dāng)前主機(jī)存在對應(yīng)的漏洞,也是可以利用類似的方式來實(shí)現(xiàn)的。

        這里我們設(shè)置要傳輸?shù)膬?nèi)容為UTF-8編碼的字符串,俄日在輸出流的write方法中通過字符串的getBytes指定其編碼,從而將其轉(zhuǎn)換為對應(yīng)的字節(jié)數(shù)組進(jìn)行發(fā)送。

        正常情況下,我們運(yùn)行服務(wù)器后再運(yùn)行客戶端,在服務(wù)器端可以得到如下輸出:

        Server is waiting for connect Hello,Java Socket Server

        Java反序列化與遠(yuǎn)程代碼執(zhí)行

        下面我們通過Java反序列化的問題來實(shí)現(xiàn)遠(yuǎn)程代碼執(zhí)行,為了實(shí)現(xiàn)遠(yuǎn)程代碼執(zhí)行,我們首先在Reader類中添加1個malicious方法,其代碼為:

        public Object malicious() throws IOException {         Runtime.getRuntime().exec("calc.exe");         System.out.println("Hacked the Server...");                 return this;     }

        在該方法中我們使用之前的介紹調(diào)用宿主機(jī)器上的計算器程序,然后輸出1個相關(guān)信息,最后返回當(dāng)前類。

        之后是對服務(wù)器端的代碼進(jìn)行如下的修改:

        while((length=input.read(bytes))!=-1) {     Reader obj = (Reader) Reader.deserialize(bytes);     obj.malicious(); }

        我們在接收到客戶端對應(yīng)的字符串后對其進(jìn)行反序列處理,之后調(diào)用某個指定的函數(shù),從而實(shí)現(xiàn)遠(yuǎn)程代碼的執(zhí)行。而在客戶端,我們需要對其進(jìn)行序列化處理:

        Reader reader = new Reader(); byte[] bytes = Reader.serialize(reader); String message = new String(bytes); output.write(message.getBytes());

        下面我們在宿主機(jī)器上運(yùn)行服務(wù)器端程序,之后在本地機(jī)器上運(yùn)行客戶端程序,當(dāng)客戶端程序執(zhí)行時,可以看到類似如下的結(jié)果:

        java反序列化引發(fā)的遠(yuǎn)程代碼執(zhí)行漏洞原理分析

        可以看到,我們成功的在宿主機(jī)器上執(zhí)行了對應(yīng)的命令執(zhí)行。

        總結(jié)

        為了實(shí)現(xiàn)通過Java的反序列問題來實(shí)現(xiàn)遠(yuǎn)程代碼執(zhí)行的漏洞,我們需要編寫1個有惡意代碼注入的序列化類。之后在客戶端將惡意代碼序列化后發(fā)送給服務(wù)器端,而服務(wù)器端需要調(diào)用我們期望的方法,從而觸發(fā)遠(yuǎn)程代碼執(zhí)行。

        為了避免服務(wù)器端進(jìn)行一些安全處理,我們可以采用反射的方式來逃逸其處理。

        這里只是1個簡化的過程,更加實(shí)用的過程可以參考Apache Common Collections的問題導(dǎo)致的Weblogic漏洞CVE-2015-4852及Jboss的漏洞CVE-2015-7501。

        推薦相關(guān)文章教程:web安全教程

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
        主站蜘蛛池模板: 亚洲AV蜜桃永久无码精品| 成人国内精品久久久久影院| 久久亚洲欧美国产精品| 91久久精品国产成人久久| 国产精品v片在线观看不卡| 中文字幕精品久久| 久久99精品久久久久久齐齐| 久久精品人人做人人爽电影| 精品无码国产污污污免费网站| 日本一区二区三区精品国产| 国产成人亚洲精品影院| 91国内外精品自在线播放| 亚洲精品国偷自产在线| 久久久久亚洲精品天堂久久久久久 | 久久精品国产亚洲AV电影| 亚洲欧洲久久久精品| 欧美精品黑人粗大欧| 精品一区二区三区免费视频| 国产成人精品免高潮在线观看| 精品视频第一页| 久久青草国产精品一区| 九九热这里只有在线精品视 | 综合精品欧美日韩国产在线| 亚洲国产91精品无码专区 | xxx国产精品视频| 国产精品多p对白交换绿帽| 国产精品特级毛片一区二区三区 | 国自产精品手机在线观看视频| 国产精品成人h片在线| 国产精品久久久天天影视香蕉| 办公室久久精品| 国产精品女同一区二区久久| 国产精品亚洲专区无码WEB| 精品久久久久久国产三级| 久久国产热这里只有精品| 久久精品18| 亚洲国产精品毛片av不卡在线| 亚洲欧洲国产精品香蕉网| 日韩精品无码熟人妻视频| 久久国产精品一国产精品金尊| 精品少妇人妻av无码久久|