如何将Servlet原理与Tomcat内存马技术结合为?
摘要:代码审计 | Servlet —— Tomcat 内存马 系列:Tomcat 内存马 —— 继 Filter 型之后,聊聊 Servlet 型的动态注入。 目录 一、Servlet 是如何注册和工作的 二、Filter 和 Servlet
代码审计 | Servlet —— Tomcat 内存马
系列:Tomcat 内存马 —— 继 Filter 型之后,聊聊 Servlet 型的动态注入。
目录
一、Servlet 是如何注册和工作的
二、Filter 和 Servlet 的区别
三、回顾对比:Filter 型 vs Servlet 型
四、前置知识:Servlet 动态注册 API
五、直接调用 addServlet 的问题
六、Servlet 型内存马注入代码(反射绕过)
七、验证步骤
八、注意事项
九、总结
一、Servlet 是如何注册和工作的
先来看一个最基础的 Servlet 示例,路径:src/main/java/org/example/filter/EchoServlet.java
package org.example.filter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//@WebServlet(value = "/echo", loadOnStartup = 1)
@WebServlet("/echo")
public class EchoServlet extends HttpServlet {
@Override
public void init() throws ServletException {
System.out.println("EchoServlet 初始化");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String msg = req.getParameter("msg");
resp.setContentType("text/plain;charset=UTF-8");
resp.getWriter().write("你输入的是:" + msg);
}
@Override
public void destroy() {
System.out.println("EchoServlet 销毁");
}
}
关于 loadOnStartup(懒加载)
@WebServlet(value = "/echo", loadOnStartup = 1) 里面有个懒加载的知识点:
loadOnStartup 默认是 -1,启用懒加载模式
规定 loadOnStartup >= 0 时,Tomcat 启动时就会加载这个 Servlet
懒加载模式下,只有第一次请求触发时才会初始化 init()
关闭懒加载后效果和 Filter 一样,启动服务时就初始化
注意:动态注册的 Servlet 默认也是懒加载(首次访问时才调用 init()),但在内存马场景中,我们通常会在注入后立即主动访问一次后门路径来激活它,因此懒加载并不影响使用。
启动服务后可以看到初始化的打印:
访问 http://localhost:8080/echo?msg=你好:
关闭时触发销毁:
二、Filter 和 Servlet 的区别
Filter 和 Servlet 最大的区别在于:
Filter 是"中间拦截器",负责预处理和后处理,通过 chain.doFilter() 继续传递请求,请求最终要到达 Servlet
Servlet 是"终点处理器",负责生成最终的响应内容
设置 @WebServlet("/echo") 后,访问 /echo 就会由这个 Servlet 处理。
如果设置 @WebServlet("/*"),那么不管访问 /xxxx 什么路径都会由这个 Servlet 处理。比如访问 http://localhost:8080/asdasd?msg=aaa:
依然可以正确显示内容,并没有 404,因为符合通配符规则。
