擅长捉弄的内存马同学:Valve内存马

news2024/9/21 2:33:05

前言

内存马的文章已经很久没有更新过了, 这篇文章不太适合想直接学习利用Valve内存马的师傅
,因为我这篇文章可能会有大篇笔墨去说Tomcat容器,至于原因就是我想更深入的了解一些Tomcat,而Valve内存马属于已经被师傅们玩烂了的一种方式,并且Valve内存马的实现如果看过之前我写的内存马文章这个内存马实现起来并不难。所以与其说这篇文章是讲Valve内存马,不如说是我学习Tomcat的个人总结,废话也不多说,我们直接开始吧。

一、Tomcat基本结构

首先我们在学习内存马之前先来重新学习一下Tomcat的基本架构。我们一层一层来进行分析。

可以看到上面我搬来的这张图,这个图就将各个结构画的很简单了,当然其中的流程还有一些步骤,这里我们慢慢说。

首先我们将结构进行单独梳理:

一个Tomcat容器中只有一个Server

一个Server可以包含多个Service

一个Service只有一个Container,但是可以有多个Connectors

一个Connector会绑定一个port和protocol

接下来我们可以将Tomcat分为两个部分,一部分为Connector,另一部分为Container。分别为图中的左右两边的部分。(这块的图片我就直接贴大哥们的图了)

Connector
:顾名思义是用于接受请求并封装Request和Response,然后交给Conntainer进行处理。它使用ProtocolHandler来处理请求。

我们来说明一下它的各个部分处理流程,首先Acceptor来监听请求(前面提到一个Service只有一个Container,但是可以有多个Connectors,这是因为请求有不同的协议,但不同的协议的字节流通过不同Connectors最终都会得到统一的处理),AsyncTimeout用于检查Request超时,当接收到请求后,提交给Handler来处理接收到的Socket并将Socket字节流发送给Processor进行处理(这之中应该有一个线程池用于提高处理速度),Processor在接收到EndPoint发送过来的Socket的后将其封装成了Tomcat
Request并交给Adapter进行处理,Adapter在接收到Tomcat
Request后将其封装为ServletRequest并将请求交给Container进行适配并进行具体的请求。

Container :Container是用来处理请求的,它通过使用Pipeline-Valve(管道-
阀门)来进行处理(我们内存马就是在这之中进行添加)

我们依旧来说明一下它的各个部分处理流程,当Container接收到Adapter发来的ServletRequest请求后首先会去调用最顶层的容器(EnginePipline)的Pipline来进行处理。之后在EnglinePipline的管道中依次执行,然后到下一个Pipline,最后到StandardWrapperValve,当执行到StandardWrapperValve后,会在StandardWrapperValve中创建FilterChain并调用doFilter方法来处理请求(这里其实看过之前三个内存马文章的师傅应该比较清楚这里)doFilter方法会依次调用所有Filter的doFilter方法和Servlet下的所有service方法,到这里Tomcat就成功处理了请求。

我们通过上面的学习,大概已经简单了解到了Tomcat的基本结构,当然这结构之中的细节其实有很多,有兴趣的同学可以深入去跟一下各个位置配置读取、请求处理和处理细节等。

二、Valve基础知识

在我们刚才的学习中已经了解到了Valve在Tomcat中所处的位置,我们的Valve实际上属于自定义Valve的位置当中,当然,在我们学习之前,我们先简单了解一下Valve的基础知识。

对于Valve的定义,其实我们通过上面的学习应该已经知道了Pipeline-Valve机制就是Container是用来处理请求的方式。

Valve:
译文为阀门,在Container的四个容器类(StandardEngine、StandardHost、StandardContext、StandardWrapper)中都有一个PipeLine和多个Vavle,和我们之前上面分析Container的图一样,而Valve顾名思义就是像阀门一样来控制我们管道当中的水流,。

在PipeLine生成时,同时会生成一个缺省Valve实现,就是我们在调试中经常看到的StandardEngine、ValveStandardHostValve、StandardContextValve、StandardWrapperValve,当各个容器类调用getPipeLine().getFirst().invoke(Request
req, Response resp)时,会首先调用用户添加的Valve,最后再调用缺省的Standard-Valve。

注意,每一个上层的Valve都是在调用下一层的Valve,并等待下层的Valve返回后才完成的,这样上层的Valve不仅具有Request对象,同时还能获取到Response对象。使得各个环节的Valve均具备了处理请求和响应的能力。

三、Valve简单实现

首先我们去看一下valve的接口

getNext方法是获取当前这个责任链节点的上一个节点。

setNext方法是设置当前这个责任链节点的上一个节点

invoke方法是逻辑处理位置

isAsyncSupported方法是表示是否支持异步

接下来我们写一个类,实现这个接口。

然后我们在server.xml配置文件中配置一下我们自定义的Valve

然后我们运行tomcat,最简单实现了这个功能

到这里就出现了一个问题,我们所有的请求走到我们自定义的valve后就断了。这是因为valve的动作定义在invoke中,通过调用this.getNext().invoke(req,
resp)将请求传入下一个valve构成了pipeline管道,如果我们不调用下一个的invoke请求到此中断,所以我们这么写是没调用到下一个invoke请求的,所以我们一般不采取实现valve接口的方法,而是选择继承ValveBase类来简单实现了valve接口,制作一个简单的valve基类。我们继承一个ValveBase来实现valve接口,接下来还是按上面的步骤来一遍。

四、Valve加载流程(Tomcat启动流程)

我们通过简单实现一个Valve,可以知道我们是通过在server.xml中设置了一个Valve,从而实现了自定义的Valve,但是Tomcat是如何读取并加载我们自定义的Valve信息的呢,接下来我们就从Tomcat的启动流程开始分析我们是如何加载我们自定义的Valve的。

首先我们先要了解一些基础知识,Tomcat启动的简单流程如下图

所以我们Tomcat是从Bootstrap开始运行的,我们跟到Tomcat的入口点开始分析,也就是Bootstrap的main方法,如下图所示,首先Bootstrap进行了实例化、初始化操作,然后调用daemon的load方法和start方法,我们直接跟进load方法。

(1)bootstrap启动流程

(2)Catalina.load()初始化操作

进入load方法,这一块我们通过反射调用了Catalina的load方法,我们继续向下跟进

(3)Digester解析server.xml

里我就不再多做赘述了,大家可以去自学一下。我们首先执行了this.createStartDigester(),这一步一句话概括就是将server.xml中的pattern、rule信息放入了cache中,将rule信息放入了rules中,当返回给digester.parse进行解析的时候,读取的节点规则信息都放到了rules下面。

当我们将节点和规则都设置完毕后,开始执行digester.parse对其开始进行解析操作,这一步实际上就是将Tomcat各组件都实例化出来(这里面其实Context比较特殊)

这里的parse的执行太多了,我就不一个一个进行跟进了,我们直接跟到解析我们自定义Valve的位置上,首先我们看一下之前的定义,可以看到匹配到/Host/Valve节点的时候最终是去触发了addValve方法(这里的匹配Object是standardHost)。

然后我们跳到解析到这个节点的位置上去,进入ContainerBase中,最终执行this.pipeline.addValve(valve),我们进行跟进

我们向下最后跟进到了StandardPipline.addValve(),这里其实就相当于按照容器作用域的配置顺序来组织valve,将每个valve都设置了指向下一个valve的next引用。我们继续向下。

我们回到Catalina然后继续向下查看,我们走到了Server初始化的位置this.getServer().init()接下来我们就快速过一下容器初始化和启动的流程然后直接到Valve的位置。

接下来就是进行JMX注册Mbean,jmx这块大家玩的应该也比较熟了,然后接下面开始循环调用service.init(),对service进行初始化,我们进去查看一下

这里对engine进行了初始化操作接下来初始化Executor线程池、Connector连接器,最后结束。

(4)Catalina.start()启动操作

到这里可能就有一些疑问了,初始化了sever、service、engine正常来说是不是应该继续初始化Host、Context、Wrapper巴拉巴拉的了,但是到了engine这里就结束了,这是为什么啊。这个的原因就是:我也不知道,但是在start方法中进行了Host组件的初始化然后进行的启动操作,所以我们回到Bootstarp去查看start方法。

回到Bootstarp,进入start方法

这里我们可以看到一路向下将我们的初始化的容器都进行了启动操作,最后我们跟到了this.engine.start()下面

我们进入this.engine.start()下面可以看到它,将子组件到children中,接下来托管给线程池处理。我们跟到这个下面。

进入sumbit,这里其实就是用到了FutureTask传入一个Callable实现,通过线程池去达到异步执行,流程就如下图所示。可以看到这里将FutrueTask的state设置为了NEW,这里的state为以下几个状态

NEW:表示一个新的任务,初始状态

COMPLETING:当任务被设置结果时,处于COMPLETING状态,这是一个中间状态。

NORMAL:表示任务正常结束。

EXCEPTIONAL:表示任务因异常而结束

CANCELLED:任务还未执行之前就调用了cancel(true)方法,任务处于CANCELLED

可能的状态过渡:

1、NEW -> COMPLETING -> NORMAL:正常结束

2、NEW -> COMPLETING -> EXCEPTIONAL:异常结束

3、NEW -> CANCELLED:任务被取消

4、NEW -> INTERRUPTING -> INTERRUPTED:任务出现中断

接下来外面就正常执行,跳转到run()下面,我们跟到这个位置

跟进run方法,我们状态为NEW,随后触发call方法,我们跟进call方法

进入call方法,我们来到了ContainerBase下面可以看到,我们执行了this.child.start(),到这里我们就执行了start()方法,我们继续一路向下

到这里我们总算是回到了生命周期的位置,而在这里我们开始了执行了init()和start(),这里其实就是就是上面我们所说的区别,我们的Server、Service、Engine都是先在init()中进行初始化,然后在start()中进行启动的,但是Host直接先初始化后运行了。这里的初始化我们简单跟进去看一下吧,实际上也没初始化啥玩意,就是在jmx里注册一下子。我们回到生命周期去执行startInternal()

跟进startInternal(),这里我们看到了,到这里其实就是在添加一些Valve,然后就去执行父类ContainerBase的startInternal方法了

五、Tomcat责任链处理流程

关于Tomcat的启动流程到Host这里我们就不在继续跟进去了,继续向下说的话就要说到我们之前的内存马文章读取configcontext那块了((*_)),跟到了这块的我们应该已经能清楚地了解到Tomcat是如何从自定义的server.xml中读取加载并最终将Valve存储到Standard中的了,接下来我们就从责任链这块都执行了什么。

我们直接来到梦开始的位置-从线程一路向下

我们来到了Adapter的文章,从这里开始我们将从connect过来的请求交给最顶层的pipline来处理也就是EngineValve没有我们自定义的Valve直接到了StandardEngineValve。

到了这里继续交给StandardHost的pipline,通过之前我们跟进的启动流程,我们可以清楚的知道first为我们自定义的Valve,直接跳到我们的Valve

所以我们就直接到达了我们的Valve下面喽,结果就是成功证明了potatosafe is trash

六、Valve内存马思路

其实跟到了这里,Valve内存马其实对我们来说就属于清晰易懂的东西,如果单纯去实现内存马的话,对于看到这个的师傅们应该就很简单了,这里我们就梳理一下思路。

我们回忆一下,我们在分析Tomcat启动流程的时候在两个位置添加过valve(我们分析的是Host),一次是在解析server.xml的时候添加了我们注册的Valve,另一次是在Host启动过程中添加了一些Valve,如下图所示

他们都通过this.getPipeline().addValve(valve)进行添加操作,所以我们也按照这个方式进行添加就OK的了,换句话说我们只要能过获取四个容器的其中一个standard,就可以通过当前容器的pipeline直接进行添加操作,添加流程如下:

1.构造一个恶意的Valve

2.通过线程获取standrad

3.通过Standard获取当前容器的Pipeline

4.添加Valve内存马

这里注意添加一下this.getNext().invoke(request, response);防止影响业务,接下来我们就实现一下.

七、Valve内存马实现

首先我们构造一个测试的demo

接下来就是常规操作了,大家这里建议自己去实现一下,和前面的差不太多。这里我搭建一个shiro的测试环境(这里我把头长度限制去掉了)

然后我们搓一个shiro反序列化打进去

然后重新访问页面,成功触发

接下来我们和之前的文章一样打一个冰鞋进去

成功连接冰鞋马,也没有影响业务请求,成功实现。

最后

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


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

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

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

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

相关文章

现场设备发生故障,如何第一时间通知相关人员?

一、前言 虹科物联网HMI作为一站式物联网解决方案,致力于解决用户在数据采集和可视化、远程监控、边缘计算、软PLC、数据存储(SQL数据库和CSV文件)、数据上云(OPC UA、MQTT)等方面的需求,帮助企业快速实现…

运营数据分析模型—用户画像

用户画像 伴随着大数据应用的讨论、创新,个性化技术成为了一个重要落地点。相比传统的线下会员管理、问卷调查、购物篮分析,大数据第一次使得企业能够通过互联网便利地获取用户更为广泛的反馈信息,为进一步精准、快速地分析用户行为习惯、消费习惯等重要商业信息,提供了足…

SpringCloud系列(十四)[分布式搜索引擎篇] - 索引库及文档的增删改查操作

本文主要介绍一下索引库及文档的一些增删改查操作, 以下都是一些常用的操作, 无需死记硬背, 只需要用到的时候常翻阅即可;   当然学习索引库和文档的一些基本操作还是要先在虚拟机启动一下 elasticsearch 及 kibana, 启动成功后输入 172.16.xx.xxx:5601 后出现以下界面即启动成…

信创-东方通和达梦适配

1 TLQ8.0 简单的例子,发送MQ,然后收消息连接是一样的,要不断去拉取数据消费的public static void main(String[] args) {//发送消息的目的队列String queName "lq";//连接工厂类QueueConnectionFactory queueConnectionFactory n…

制造业仓库很乱,该如何有效的管理呢?

首先来统计一下制造业仓库很乱的问题: 1.管理多仓库/多店铺,库存不清2.库存控制不合理,出现滞销与脱销的情况3.库存盘点时间长,数据不准确在传统管理模式下,一些中小型制造业的仓库,物料摆放位置、明细等都…

TOOLS_Seaborn相关性可视化分析示例

TOOLS_Seaborn相关性可视化分析示例 相关性应用场景 如要回答 推测 网站留存、观看时长、收藏次数、转发次数、关注数之间是否存在相关性,以及相关性有多大的问题; 对于更有关注的留存,可以尝试分析一个与其相关性比较大的因素&#xff0c…

【大数据专题】大数据理论基础01之分布式CPA原理深入理解

分布式CAP定理 CAP定理又称CAP原则,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),最多只能同时三个特性中…

编写一个Vue插件,上传NPM官网开源使用

插件开发及上传NPM流程 完成组件封装、组件完成统一封装成插件插件入口文件配置src/main.js 中引入口文件、安装插件配置 package.json 文件npm run lib 打包umd.min.js 配置到 package.json登录 NPM 上传插件npm i 安装引入使用 一、编写好组件 二、插件入口文件配置 plugins/…

更安全更稳定,阿里云斩获多项云系统稳定安全运行优秀案例

近日,阿里云凭借在稳定性领域的全栈投入,获评中国信通院混沌工程实验室 2022 年度杰出贡献企业,并斩获“云系统稳定安全运行优秀案例”活动中多领域优秀案例。阿里云持续推动企业 IT 系统建设,保障千行百业安全稳定的实现数字化转…

普通人如何用AI帮你干活——娱乐1

案例描述:一天朋友突然发信息跟我说:看你朋友全最近总在显摆AI作画,看起来好像有点牛逼;我家小朋友最近在参加一个国家绘画比赛,能不能让你的AI帮忙画几张有趣的有创意的画。给小朋友做灵感启发,小朋友可以…

如何处理负面评论?利用负面评论发挥优势

每家公司都应该做的一件事:回复评论! 37%的买家积极考虑对评论的回应,以评估和对品牌的看法。所以不要忘记回复评论! 如何处理负面评论 如果您的公司正在经历大量负面评论,请了解您的产品团队如何利用它们来发挥自己的…

CSS隐藏元素、BFC、元素居中、布局

1、css中有哪些方式可以隐藏页面元素?区别是什么?1.1 display:none元素在页面上将彻底消失,元素本身占有的空间会被其他元素占有,导致浏览器的重排和重绘。特点:元素不可见,不占据空间,不会触发…

面试阿里自动化测试工程师被狂虐,回家猛补3个月,成功上岸字节

前言 大家好,我是小祖,个人背景:985中下游,无大厂实习(小厂打杂),无竞赛,无相关论文。 毕业几年,表面上用过很多技术,但都没能深入学习,一年的工…

python 支付宝营销活动现金红包开发接入流程-含接口调用加签

1 创建网页/移动应用 2 配置接口加签方式 涉及到金额的需要上传证书,在上传页面有教程, 在支付宝开放平台秘钥工具中生成CSR证书,会自动保存应用公钥和私钥到电脑上,调用支付宝接口需要应用私钥进行加签 上传完CSR证书后会有三个…

互联网衰退期,测试工程师35岁的路该怎么走...

国内的互联网行业发展较快,所以造成了技术研发类员工工作强度比较大,同时技术的快速更新又需要员工不断的学习新的技术。因此淘汰率也比较高,超过35岁的基层研发类员工,往往因为家庭原因、身体原因,比较难以跟得上工作…

DSP_TMS320F28335_PIE学习笔记

前言 本文重点探讨DSP PIE模块的学习笔记,由于学这部内容的时候,是用28335学的,所以标题是用的28335,但其实28377D和28335的PIE使用基本上是一样的,也是可以借鉴的。 正文 原理 讲点原理,PIE&#xff0…

linux minio更改密码MINIO_ACCESS_KEY报错

minio版本RELEASE.2020-11-13T20-10-18Z启动文件配置如下cat run.sh#!/bin/bashexport MINIO_ACCESS_KEYminioexport MINIO_SECRET_KEYfasffnohup /opt/minio/minio.RELEASE.2020-11-13T20-10-18Z server http://192.168.100.x/data/minio_data http://192.168.100.x/data/mini…

Docker中对已存在运行的容器修改端口映射

一、初次创建容器,指定宿主机和容器端口的映射,如下示例: docker run -itd -p 11935:1935 -p 11985:1985 -p 18080:8080 -p 20903:20903 -p 18888:8888 \ --restartalways \ -v /srv/srs3/conf/:/usr/local/srs/conf/ \ -v /srv/srs3/objs/:…

Android总结汇总

一、APP1、混合开发项目AHP地址:https://github.com/Witnin/AHP简介:KotlinJava二、路由框架1、集成ARouter导航框架官网地址(停止维护):https://github.com/alibaba/ARouter/blob/master/README_CN.mdjadepeakpoet&am…

Leedcode 1011. 在 D 天内送达包裹的能力

题目 传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。 传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量(weights)的顺序往传送带上装载包裹。我们装载的重量不会超过船的最大运载重量。 返回能在 day…