一文读懂Java内存马——Filter篇
文章介绍了Filter型内存马的特点及其作为攻击手段的应用,并详细讲解了Java Web中Filter组件的功能、加载流程及实现方法,通过实例代码展示了如何利用Filter进行XSS攻击防御。 2025-10-14 00:55:54 Author: www.freebuf.com(查看原文) 阅读量:3 收藏

前言

接上文servlet:内存马--是一种无文件、存活于服务器内存中的恶意程序,其中Filter型内存马因其隐蔽性和拦截请求的能力,成为常见的攻击手段,与传统得一句话木马相比,内存马得恶意代码直接注入Web服务器进程内存(如Tomcat、Spring容器)中运行,仅通过内存驻留实现远程控制‌。

认识filter过滤器

在Java Web中,Filter(过滤器)是Servlet规范中定义的一种组件,它可以对Web应用程序的请求和响应进行预处理和后处理。Filter在请求到达Servlet之前拦截请求,在响应返回给客户端之前拦截响应。Filter可以用于很多场景,比如日志记录、安全控制(比如常见的XSS防护等)、字符编码转换、数据压缩等。

Filter的作用:

认证和授权:例如,检查用户是否登录,是否有权限访问某个资源。

日志记录:记录请求的详细信息,如IP地址、请求的URL、处理时间等。

数据转换:如统一设置请求和响应的字符编码。

压缩响应:对响应内容进行压缩,减少网络传输量。

加密解密:对请求或响应进行加密解密处理。

触发事件:在请求处理前后触发一些事件。

Filter的加载过程:

1.启动Web应用时:容器(如Tomcat)会读取web.xml(或通过注解@WebFilter)中配置的Filter,并按照配置的顺序加载和初始化Filter。每个Filter都会调用其init方法,进行一些初始化操作。

2.请求到达时:当请求到达容器时,容器会根据web.xml中配置的URL映射,决定哪些Filter需要被执行。这些Filter会按照配置的顺序组成一个过滤器链(FilterChain)

3.执行过滤器链:容器会依次调用链中每个Filter的doFilter方法,并将请求和响应对象作为参数传递,同时传递过滤器链对象。在Filter的doFilter方法中,开发者可以对请求和响应进行处理,然后决定是否继续执行过滤器链。通过调用FilterChain.doFilter(request, response)可以将请求传递给下一个Filter或最终的Servlet。如果在某个Filter中没有调用FilterChain.doFilter,那么请求就不会继续传递,即后续的Filter和Servlet都不会执行。

4.响应返回时:当过滤器链执行完毕,响应会以相反的顺序再次经过这些Filter(这里指的是在doFilter方法中,调用FilterChain.doFilter之后的代码,这些代码会在响应返回时执行)。因此,Filter可以对响应进行后处理。

自己动手写filter

既然知道了filter的作用,现在通过实例代码来看看具体的filter如何实现:

通过idea创建一个web项目:具体配置见上一篇servlet内存马内容,项目结构如下:

1760338142_68eca0de2e4ec25d68411.png!small?1760338142557

首先要写一个filter类必须继承HttpFilter,然后重写它的doFilter方法,在方法中实现我们自己的代码逻辑即可,新建一个filter类,firstFilter.java,代码如下:在下面的测试代码中,测试简单的防御xss攻击,当get请求中包含script,即返回一句话。

package com.ex;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpFilter;
import java.io.IOException;

public class firstFilter extends HttpFilter {
    @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String cmd = request.getParameter("cmd");
        if (cmd.contains("script")){
            response.getWriter().println("XSS--atack");
        }else {
            super.doFilter(request, response, chain);
}
    }
    @Override
public void destroy() {
    }
}

然后在web.xml中写入对应的filter配置信息,如下图:<url-pattern>/*</url-pattern>表示所有的url均会受到这个过滤器的影响。

1760338396_68eca1dc9f089c8eeca05.png!small?1760338397158

现在我们就实现了我们自己的第一个filter。启动项目,然后运行一下,任何页面get请求,cmd参数中只要包含scrpit就会执行我们的代码:在实际应用中,就可通过代码逻辑,达到拦截恶意攻击的效果。

1760338436_68eca204e8f2f50c4567d.png!small?17603384372711760338447_68eca20fa9a0b60799cf5.png!small?1760338447983

filter小结:访问web的时候,会根据xml中配置的url-pattern影响范围,执行对应的过滤器filter。

filter加载流程

在firstFilter.java中的doFilter打上断点:运行程序,访问web,cmd参数只要不包含script就可以执行到下面的doFilter,比如访问:http://127.0.0.1:8080/Memory/?cmd=aaaa即可执行doFilter()。先看看参数request中有什么,如下图:

1760344284_68ecb8dcc10a2189be59a.png!small?1760344285215

有一个属性filterChain,里面的分别是我们自己的filter,在容器解析web.xml时赋值。以及tomcat的默认filter(WsFilter 是 Tomcat 内部用于处理WebSocket 升级请求的一个过滤器。当客户端发起 WebSocket 连接请求时,这个过滤器会拦截该请求,并协助完成从 HTTP 协议到 WebSocket 协议的升级握手),启动时加入,暂不关注其具体细节。

把执行的流程往前推一推,看看到底是怎么一步步执行到我们自己的doFilter():

1760344416_68ecb9600b6d856853204.png!small?1760344416521

是一个变量filterChain的doFilter方法调用的-如下图:

1760344681_68ecba69a1b695f873239.png!small?1760344682122

而这里的 ApplicationFilterChain filterChain= ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); 是一个ApplicationFilterChain 类型,通过createFilterChain之后,得到的就是上图所示的,有个filters属性,里面就是当前应用中的filter ,但是是封装在ApplicationFilterConfig中的--它的主要作用是‌封装和管理filter过滤器的配置信息‌,在Servlet 容器启动时动态加载和初始化过滤器。

看跟进这个doFilter方法里面:如下图

1760345213_68ecbc7d3b71310fad763.png!small?1760345213721

里面只有一个逻辑,就是不管true或者false,都执行它的internalDoFilter()方法:

1760345238_68ecbc962cd2be634dfd7.png!small?1760345238723

上图所示:在internalDoFilter()方法中, if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter();

判断是否数组是否越界,pos<n 从filters数组中获取一个ApplicationFilterConfig :如下图,pos和n初始为0,这里的n表示数组长度,0表示数组索引,调用之后自增(filters[pos++]):引用启动之后,n会根据读取到的filter数量,对应递增,这里为2。

1760345325_68ecbced31a2929f517d5.png!small?1760345325716

然后获取到这个刚从数组中拿出来的filter,这里的filter就是我们的filter(firstFilter)。

1760345344_68ecbd000bf177a717ed2.png!small?1760345344500

然后再后面继续执行 filter.doFilter(request, response, this);执行到重写方法!

父类的doFilter方法:super.doFilter(request, response, chain);

1760345771_68ecbeab895b5d18d8b22.png!small?1760345771892

里面再次调用doFilter():

1760345792_68ecbec044691a1537bb3.png!small?1760345792863

再次跟进发现又回到了internalDoFilter()方法:


文章来源: https://www.freebuf.com/articles/web/452490.html
如有侵权请联系:admin#unsafe.sh