Tomcat技术栈
■ Tomcat 实现的 2 个核心功能:
1.处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。
2.加载并管理 Servlet ,以及处理具体的 Request 请求。
所以 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)。
连接器负责对外交流,容器负责内部处理
Tomcat为了实现支持多种 I/O 模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。
Server 对应的就是一个 Tomcat 实例。
Service 默认只有一个,也就是一个 Tomcat 实例默认一个 Service。
Connector:一个 Service 可能多个 连接器,接受不同连接协议。
Container: 多个连接器对应一个容器,顶层容器其实就是 Engine。
每个组件都有对应的生命周期,需要启动,同时还要启动自己内部的子组件,比如一个 Tomcat 实例包含一个 Service,一个 Service 包含多个连接器和一个容器。而一个容器包含多个 Host, Host 内部可能有多个
Context 容器,而一个 Context 也会包含多个 Servlet,所以 Tomcat 利用组合模式管理组件每个组件,对待过个也想对待单个组一样对待。整体上每个组件设计就像是「俄罗斯套娃」一样。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
■ Tomcat 实现的 2 个核心功能:
1.处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。
2.加载并管理 Servlet ,以及处理具体的 Request 请求。
所以 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)。
连接器负责对外交流,容器负责内部处理
Tomcat为了实现支持多种 I/O 模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。
Server 对应的就是一个 Tomcat 实例。
Service 默认只有一个,也就是一个 Tomcat 实例默认一个 Service。
Connector:一个 Service 可能多个 连接器,接受不同连接协议。
Container: 多个连接器对应一个容器,顶层容器其实就是 Engine。
每个组件都有对应的生命周期,需要启动,同时还要启动自己内部的子组件,比如一个 Tomcat 实例包含一个 Service,一个 Service 包含多个连接器和一个容器。而一个容器包含多个 Host, Host 内部可能有多个
Context 容器,而一个 Context 也会包含多个 Servlet,所以 Tomcat 利用组合模式管理组件每个组件,对待过个也想对待单个组一样对待。整体上每个组件设计就像是「俄罗斯套娃」一样。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
.Processor组件
Processor 用来实现 HTTP 协议,Processor 接收来自 EndPoint的 Socket,读取字节流解析成 Tomcat Request和 Response对象,并通过 Adapter将其提交到容器处理,Processor是对应用层协议的抽象。
从图中我们看到,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方调用 HttpProcessor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法,方法内部通过 以下代码将请求传递到容器中。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
Processor 用来实现 HTTP 协议,Processor 接收来自 EndPoint的 Socket,读取字节流解析成 Tomcat Request和 Response对象,并通过 Adapter将其提交到容器处理,Processor是对应用层协议的抽象。
从图中我们看到,EndPoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 Run 方调用 HttpProcessor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法,方法内部通过 以下代码将请求传递到容器中。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
Tomcat 容器
1.首先根据协议和端口号确定连接器。
Tomcat 默认的 HTTP 连接器监听 8080 端口、默认的 AJP 连接器监听 8009 端口。上面例子中的 URL 访问的是 8080 端口,因此这个请求会被 HTTP 连接器接收,而一个连接器是属于一个 Service 组件的,这样 Service 组件就确定了。我们还知道一个 Service 组件里除了有多个连接器,还有一个容器组件,具体来说就是一个 Engine 容器,因此 Service 确定了也就意味着 Engine 也确定了。
2.根据域名选定 Host(站点)
Service 和 Engine 确定后,Mapper 组件通过 URL 中的域名去查找相应的 Host 容器,比如例子中的 URL 访问的域名是user.shopping.com,因此 Mapper 会找到 Host2 这个容器。
3.根据 URL 路径找到 Context 组件(Web应用)
Host 确定以后,Mapper 根据 URL 的路径来匹配相应的 Web 应用的路径,比如例子中访问的是 /order,因此找到了 Context4 这个 Context 容器。
4.根据 URL 路径找到 Wrapper(Servlet)
Context 确定后,Mapper 再根据 web.xml 中配置的 Servlet 映射路径来找到具体的 Wrapper 和 Servlet。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
1.首先根据协议和端口号确定连接器。
Tomcat 默认的 HTTP 连接器监听 8080 端口、默认的 AJP 连接器监听 8009 端口。上面例子中的 URL 访问的是 8080 端口,因此这个请求会被 HTTP 连接器接收,而一个连接器是属于一个 Service 组件的,这样 Service 组件就确定了。我们还知道一个 Service 组件里除了有多个连接器,还有一个容器组件,具体来说就是一个 Engine 容器,因此 Service 确定了也就意味着 Engine 也确定了。
2.根据域名选定 Host(站点)
Service 和 Engine 确定后,Mapper 组件通过 URL 中的域名去查找相应的 Host 容器,比如例子中的 URL 访问的域名是user.shopping.com,因此 Mapper 会找到 Host2 这个容器。
3.根据 URL 路径找到 Context 组件(Web应用)
Host 确定以后,Mapper 根据 URL 的路径来匹配相应的 Web 应用的路径,比如例子中访问的是 /order,因此找到了 Context4 这个 Context 容器。
4.根据 URL 路径找到 Wrapper(Servlet)
Context 确定后,Mapper 再根据 web.xml 中配置的 Servlet 映射路径来找到具体的 Wrapper 和 Servlet。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
❤1
TestServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().write("hello world");
}
}
然后配置项目运行所需的 tomcat 环境
然后配置 artifacts ,直接点击 fix,然后添加 web
1.0.1.servlet 初始化流程分析
我们在org.apache.catalina.core.StandardWrapper#setServletClass处下断点调试
我们尝试按Ctrl+左键追踪它的上层调用位置,但是提示我们找不到,需要按两次 Ctrl+Alt+F7
然后就可以看到,上层调用位置位于
接下来我们详细看下面这段代码
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.getWriter().write("hello world");
}
}
然后配置项目运行所需的 tomcat 环境
然后配置 artifacts ,直接点击 fix,然后添加 web
1.0.1.servlet 初始化流程分析
我们在org.apache.catalina.core.StandardWrapper#setServletClass处下断点调试
我们尝试按Ctrl+左键追踪它的上层调用位置,但是提示我们找不到,需要按两次 Ctrl+Alt+F7
然后就可以看到,上层调用位置位于
接下来我们详细看下面这段代码
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
通过webxml.getServlets()获取的所有 Servlet 定义,并建立循环;然后创建一个 Wrapper 对象,并设置 Servlet 的加载顺序、是否启用(即获取</load-on-startup>标签的值)、Servlet 的名称等基本属性;接着遍历 Servlet 的初始化参数并设置到Wrapper 中,并处理安全角色引用、将角色和对应链接添加到 Wrapper 中;如果 Servlet 定义中包含文件上传配置,则根据配置信息设置MultipartConfigElement;设置 Servlet 是否支持异步操作;通过context.addChild(wrapper);将配置好的 Wrapper 添加到Context 中,完成 Servlet 的初始化过程。
上面大的 for 循环中嵌套的最后一个 for 循环则负责处理 Servlet 的 url 映射,将 Servlet 的 url 与 Servlet 名称关联起来。
也就是说,Servlet 的初始化主要经历以下六个步骤:
● 创建 Wapper 对象;
● 设置 Servlet 的 LoadOnStartUp 的值;
● 设置 Servlet 的名称;
● 设置 Servlet 的 class;
● 将配置好的 Wrapper 添加到 Context 中;
● 将 url 和 servlet 类做映射
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
上面大的 for 循环中嵌套的最后一个 for 循环则负责处理 Servlet 的 url 映射,将 Servlet 的 url 与 Servlet 名称关联起来。
也就是说,Servlet 的初始化主要经历以下六个步骤:
● 创建 Wapper 对象;
● 设置 Servlet 的 LoadOnStartUp 的值;
● 设置 Servlet 的名称;
● 设置 Servlet 的 class;
● 将配置好的 Wrapper 添加到 Context 中;
● 将 url 和 servlet 类做映射
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
Filter 组件(过滤器)
(与 FilterDefs、FilterConfigs、FilterMaps、FilterChain)
从下图可以看出,这个filter就是一个关卡,客户端的请求在经过filter之后才会到Servlet,那么如果我们动态创建一个filter并且将其放在最前面,我们的filter就会最先执行,当我们在filter中添加恶意代码,就可以实现命令执行,形成内存马。
1. 首先,需要定义过滤器FilterDef,存放这些FilterDef的数组被称为FilterDefs,每个FilterDef定义了一个具体的过滤器,包括描述信息、名称、过滤器实例以及class等,这一点可以从org/apache/tomcat/util/descriptor/web/FilterDef.java的代码中看出来;
2. 然后是FilterDefs,它只是过滤器的抽象定义,而FilterConfigs则是这些过滤器的具体配置实例,我们可以为每个过滤器定义具体的配置参数,以满足系统的需求;
3. 紧接着是FilterMaps,它是用于将FilterConfigs映射到具体的请求路径或其他标识上,这样系统在处理请求时就能够根据请求的路径或标识找到对应的FilterConfigs,从而确定要执行的过滤器链;而FilterChain是由多个FilterConfigs组成的链式结构,它定义了过滤器的执行顺序,在处理请求时系统会按照FilterChain中的顺序依次执行每个过滤器,对请求进行过滤和处理。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
(与 FilterDefs、FilterConfigs、FilterMaps、FilterChain)
从下图可以看出,这个filter就是一个关卡,客户端的请求在经过filter之后才会到Servlet,那么如果我们动态创建一个filter并且将其放在最前面,我们的filter就会最先执行,当我们在filter中添加恶意代码,就可以实现命令执行,形成内存马。
1. 首先,需要定义过滤器FilterDef,存放这些FilterDef的数组被称为FilterDefs,每个FilterDef定义了一个具体的过滤器,包括描述信息、名称、过滤器实例以及class等,这一点可以从org/apache/tomcat/util/descriptor/web/FilterDef.java的代码中看出来;
2. 然后是FilterDefs,它只是过滤器的抽象定义,而FilterConfigs则是这些过滤器的具体配置实例,我们可以为每个过滤器定义具体的配置参数,以满足系统的需求;
3. 紧接着是FilterMaps,它是用于将FilterConfigs映射到具体的请求路径或其他标识上,这样系统在处理请求时就能够根据请求的路径或标识找到对应的FilterConfigs,从而确定要执行的过滤器链;而FilterChain是由多个FilterConfigs组成的链式结构,它定义了过滤器的执行顺序,在处理请求时系统会按照FilterChain中的顺序依次执行每个过滤器,对请求进行过滤和处理。
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
❤1
.内存马实现
根据上面的调试,我们发现最关键的就是StandardContext.findFilterMaps()和StandardContext.findFilterConfig(),我们可以来看看这2个方法的实现,可以看到都是直接从StandardContext中取到对应的属性,那么我们只要往这2个属性里面插入对应的filterMap和filterConfig即可实现
动态添加filter的目的
实际上StandardContext也有一些方法可以帮助我们添加属性。首先我们来看filtermaps,StandardContext直接提供了对应的添加方法(Before是将filter放在首位,正是我们需要的),这里再往filterMaps添加之前会有一个校验filtermap是否合法的操作,跟进validateFilterMap()
可以看到这里有一个坑点,它会根据filterName去寻找对应的filterDef,如果没找到的话会直接抛出异常,也就是说我们还需要往filterDefs里添加filterDef。
我们接下来再看filterDefs,StandardContext直接提供了对应的添加方法
最后我们再来看filterConfigs,根据命名规则搜索addFilterConfig,发现并没有这个方法,所以我们考虑要通过反射的方法手动获取属性并添加
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
根据上面的调试,我们发现最关键的就是StandardContext.findFilterMaps()和StandardContext.findFilterConfig(),我们可以来看看这2个方法的实现,可以看到都是直接从StandardContext中取到对应的属性,那么我们只要往这2个属性里面插入对应的filterMap和filterConfig即可实现
动态添加filter的目的
实际上StandardContext也有一些方法可以帮助我们添加属性。首先我们来看filtermaps,StandardContext直接提供了对应的添加方法(Before是将filter放在首位,正是我们需要的),这里再往filterMaps添加之前会有一个校验filtermap是否合法的操作,跟进validateFilterMap()
可以看到这里有一个坑点,它会根据filterName去寻找对应的filterDef,如果没找到的话会直接抛出异常,也就是说我们还需要往filterDefs里添加filterDef。
我们接下来再看filterDefs,StandardContext直接提供了对应的添加方法
最后我们再来看filterConfigs,根据命名规则搜索addFilterConfig,发现并没有这个方法,所以我们考虑要通过反射的方法手动获取属性并添加
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
❤2
在Tomcat中与Filter密切相关的几个类:1.FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息2.FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息3.FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern4.FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter5.WebXml:存放 web.xml 中内容的类6.ContextConfig:Web应用的上下文配置类★7.StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper★8.StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet
最后总结下Filter型内存马(即动态创建filter)的步骤:
1. 获取StandardContext
2. 继承并编写一个恶意filter
3. 实例化一个FilterDef类,包装filter并存放到StandardContext.filterDefs中
4. 实例化一个FilterMap类,将我们的 Filter 和 urlpattern 相对应,存放到StandardContext.filterMaps中(一般会放在首位)
5. 通过反射获取filterConfigs,实例化一个FilterConfig(ApplicationFilterConfig)类,传入StandardContext与filterDefs,存放到filterConfig中
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke
最后总结下Filter型内存马(即动态创建filter)的步骤:
1. 获取StandardContext
2. 继承并编写一个恶意filter
3. 实例化一个FilterDef类,包装filter并存放到StandardContext.filterDefs中
4. 实例化一个FilterMap类,将我们的 Filter 和 urlpattern 相对应,存放到StandardContext.filterMaps中(一般会放在首位)
5. 通过反射获取filterConfigs,实例化一个FilterConfig(ApplicationFilterConfig)类,传入StandardContext与filterDefs,存放到filterConfig中
/网站渗透 /黑客编程技术
/软件渗透 /提取数据
/网站权限 /网站入侵破解
有需求的找我 @MoA_Ke