從安全角度談Java反射機制--前章

前言

首發:https://www.sec-in.com/article/307
   歡迎回來,上回說到Java反射機制基礎知識,並用簡單代碼做了一個簡單的Demo。
  筆者從執行命令的角度來展開話題,看這篇文章大部分都是網絡安全的從業者亦或者是安全規範的學習者,衆所周知執行命令獲取權限是安全人員追求,也是黑客最終追求。所以從執行命令的角度來展開,無疑是激發你們讀者最大閱讀興趣。

ps: 本文實驗代碼都上傳JavaLearnVulnerability項目,爲了讓更多人知道,麻煩動動小手star一下。


知識補充

   這裏稍微補充一點小知識,對於一些0基礎的讀者的一些知識補充,老司機可以忽視。Java執行命令常見的有兩個類java.lang.Runtimejava.lang.ProcessBuilder,通常情況下使用java.lang.Runtime類,個人覺得Runtime類是比ProcessBuilder好使用些。

windows下執行命令的幾種方式

  1. Windows下調用程序

Process proc =Runtime.getRuntime().exec(“exefile”);

  1. Windows下調用系統命令

String [] cmd={“cmd”,"/C",“copy exe1 exe2”};
Process proc =Runtime.getRuntime().exec(cmd);

  1. Windows下調用系統命令並彈出命令行窗口

String [] cmd={“cmd”,"/C",“start copy exe1 exe2”};
Process proc =Runtime.getRuntime().exec(cmd);

Linux下執行命令的幾種方式

  1. Linux下調用程序

Process proc =Runtime.getRuntime().exec("./exefile");

  1. Linux下調用系統命令

String [] cmd={"/bin/sh","-c",“ln -s exe1 exe2”};
Process proc =Runtime.getRuntime().exec(cmd);

  1. Linux下調用系統命令並彈出命令行窗口

String [] cmd={"/bin/sh","-c",“xterm -e ln -s exe1 exe2”};
Process proc =Runtime.getRuntime().exec(cmd);


反射之Runtime

在這裏插入圖片描述

方法一

Object ob = cls.getMethod(“getRuntime”,null).invoke(null,null);
返回的是一個Runtime類對象
前文說過,invoke第一個參數是一個Object類對象
你可能好奇爲什麼我不直接使用newInstance()呢?
因爲Runtime類中的無參構造方法是private權限,無法訪問。ps:其實是有辦法的,下文會說道。
getRuntime方法返回的是Runtime類對象,這就是爲什麼有些payload會使用此方法了,而不是直接newInstanc()

在這裏插入圖片描述

public static void Method1(){
        try {
            //獲取對象
            Class cls = Class.forName("java.lang.Runtime");
            //實例化對象
            Object ob = cls.getMethod("getRuntime",null).invoke(null,null);
            // 反射調用執行命令
            cls.getMethod("exec", String.class).invoke(ob,"calc");


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

方法二

前文說過java.lang.Runtime類無參構造方法是private權限無法直接調用
setAccessible通過反射修改方法的訪問權限,強制可以訪問
Constructor constructor = cls.getDeclaredConstructor();是獲取類構造器方法的方法

 public static void Method2(){
        try {
            // 獲取對象
            Class cls = Class.forName("java.lang.Runtime");
            // 獲取構造方法
            Constructor constructor = cls.getDeclaredConstructor();
            
            constructor.setAccessible(true);
            // 實例化對象
            Object ob = constructor.newInstance();
            Method mt = cls.getMethod("exec", String.class);

            mt.invoke(ob,"calc");


        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

反射之ProcessBuilder

在這裏插入圖片描述

通過看JDK文檔,ProcessBuilder類有兩個構造方法。
在這裏插入圖片描述
如果分別使用反射構造方法獲取實例化語句如下:

  • Class.forName("java.lang.ProcessBuilder").getDeclaredConstructor(List.class).newInstance(Arrays.asList("calc")))
  • Class.forName("java.lang.ProcessBuilder").getDeclaredConstructor(String.class).newInstance("calc"))

方法一

   方法一筆者在此就提一點,newInstance()實例化時把所需要執行命令參數直接一併進行了。

public static void Method1(){
        try {
            // 獲取對象
            Class cls = Class.forName("java.lang.ProcessBuilder");
            // 實例化對象
            Object ob = cls.getDeclaredConstructor(List.class
            ).newInstance(Arrays.asList("calc"));
            // 執行命令
            cls.getMethod("start").invoke(ob,null);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

方法二

   方法二使用第二種構造方法,可變長參數(String...表示參數長度不確定)。那麼對於反射來說,如果要獲取目標函數裏包含的可變長參數,可直接視爲數組。因此只需要將String [].class傳給構造方法即可,但在調用newInstance()實例化方法時,不能直接傳一個一維數組String[]{“calc"},而是應該傳入一個二維數組String[][]{{"calc"}}。因爲newInstance()函數本身接收的是一個可變長參數,我們傳給ProcessBuilder的也是一個可變長參數,二者疊加由一維數組變成了二維數組。

public static void Method2(){
        try {
            // 獲取對象
            Class cls = Class.forName("java.lang.ProcessBuilder");
            // 實例化對象
            
            String[][] cls2 = new String[][]{{"calc"}};
            Object ob = cls.getConstructor(String[].class).newInstance(cls2);
            //執行命令
            cls.getMethod("start").invoke(ob,null);

        } catch (ClassNotFoundException | NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

參考

https://xz.aliyun.com/t/7029#toc-3
https://javasec.org/javase/Reflection/Reflection.html
JDK文檔
Java安全漫談-反射篇 --P牛著。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章