天天看点

Tomcat内存马学习(一)Filter型

想研究内存马还是得学习这方面的知识。对于tomcat容器的详解参考:

tomca结构

tomcat总体结构如下。

server –> service –> connector & container( engine –> host –> context( wrapper( servlet ) ) )
Tomcat内存马学习(一)Filter型

下面这个图是我对tomcat的理解。如有错误请指出。

Tomcat内存马学习(一)Filter型

这我就说一下我对tomcat的理解。

server的任务就是提供一个接口让其它程序能够访问到这个 service 集合、同时要维护它所包含的所有 service 的生命周期,包括如何初始化、如何结束服务、如何找到别人要访问的 service。还有其它的一些次要的任务。

service主要包含两个组件​<code>​connector​</code>​和​<code>​container​</code>​。connector 主要负责对外交流,container 主要处理 connector 接受的请求,主要是处理内部事务。service 只是在 connector 和 container 外面多包一层,把它们组装在一起,向外面提供服务,一个 service 可以设置多个 connector,但是只能有一个 container 容器

connector将在某个指定的端口上来监听客户的请求,把从socket传递过来的数据,封装成request,传递给engine来处理,并从engine处获得响应并返回给客户。

tomcat通常会用到两种connector:

http connector 在端口8080处侦听来自客户browser的http请求。 ajp connector

在端口8009处侦听来自其它webserver(apache)的servlet/jsp代理请求。

container是一个接口,定义了下属的各种容器,尤其是​<code>​wrapper、host、engine、context​</code>​。

Tomcat内存马学习(一)Filter型

负责处理来自相关联的service的所有请求,处理后,将结果返回给service。

一个engine下可以配置一个默认主机,每个虚拟主机都有一个域名。当engine获得一个请求时,它把该请求匹配到虚拟主机(host)上,然后把请求交给该主机来处理。

engine有一个默认主机,当请求无法匹配到任何一个虚拟主机时,将交给默认host来处理。engine以线程的方式启动host。

代表一个虚拟主机,每个虚拟主机和某个网络域名(domain name)相匹配。就例如图中的a.com,b.com。

每个虚拟主机下都可以部署一个或多个web应用,每个web应用对应于一个context,有一个context path。

当host获得一个请求时,将把该请求匹配到某个context上,然后把该请求交给该context来处理匹配的方法是“最长匹配”,所以一个path==””的context将成为该host的默认context。

所有无法和其它context的路径名匹配的请求都将最终和该默认context匹配。

一个context对应于一个web应用,一个web应用由一个或者多个servlet组成。

这里说一下context对应的web应用, 每一个context都有唯一的path, 这里的path不是指servlet绑定的webservlet地址, 而是指的是独立的一个web应用地址, 就好比tomat默认的/地址和manager地址就是两个不同的web应用, 所以对应两个不同的context, 要添加context需要在server.xml中配置docbase。

当context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的servlet类,如果找到,则执行该类,获得请求的回应,并返回。

wrapper 代表一个 servlet,它负责管理一个 servlet,包括的 servlet 的装载、初始化、执行以及资源回收。wrapper 是最底层的容器,它没有子容器了

其实,wrapper就是对servlet的封装。

了解tomcat之后,就要对内存马进行研究了。

内存马主要分如下几类:

servlet-api类

filter型

servlet型

spring类

拦截器

controller型

java instrumentation类

agent型

这里主要讲一下​<code>​filter型​</code>​的内存马,先放一张关于filter的简单的图。如果没学过servlet和filter建议还是先看看。

Tomcat内存马学习(一)Filter型

可以看出,用户的请求会先依次经过filter、filter2然后到达servlet。然后返回结果的时候也是依次经过filter2、filter然后到达客户。

这里分析一下filter在tomcat中的流程是怎么样的。

首先新建一个servlet。然后创建filter

测试servlet

测试filter

在web.xml中配置。

先简单介绍一下关键的几个类,摘自http://wjlshare.com/archives/1529

​<code>​filterdefs​</code>​:存放filterdef的数组 ,filterdef 中存储着我们过滤器名,过滤器实例,作用 url 等基本信息。

​<code>​filterconfigs​</code>​:存放filterconfig的数组,在 filterconfig 中主要存放 filterdef 和 filter对象等信息。

​<code>​filtermaps​</code>​:存放filtermap的数组,在 filtermap 中主要存放了 filtername 和 对应的urlpattern。

​<code>​filterchain​</code>​:过滤器链,该对象上的 dofilter 方法能依次调用链上的 filter。

​<code>​webxml​</code>​:存放 web.xml 中内容的类。

​<code>​contextconfig​</code>​:web应用的上下文配置类。

​<code>​standardcontext​</code>​:context接口的标准实现类,一个 context 代表一个 web 应用,其下可以包含多个 wrapper。

​<code>​standardwrappervalve​</code>​:一个 wrapper 的标准实现类,一个 wrapper 代表一个servlet。

在项目设置里添加好相关类所需要的jar包,方便调试。

Tomcat内存马学习(一)Filter型

即可在external libranes中找到。

Tomcat内存马学习(一)Filter型

现在就来分析一下filter的执行流程。

双击shift键,全局搜索​<code>​standardwrappervalve​</code>​类,找到​<code>​applicationfilterfactory.createfilterchain(request, wrapper, servlet);​</code>​关键代码。

Tomcat内存马学习(一)Filter型

​<code>​applicationfilterfactory​</code>​会创建filterchain(filter链)这个链就是我们经常在filter中使用的filterchain。调用其dofilter方法就会跑到下一个filter。跟进这个​<code>​createfilterchain​</code>​方法。

Tomcat内存马学习(一)Filter型

首先通过​<code>​wrapper​</code>​调用​<code>​getparent​</code>​方法获取当前context。然后通过​<code>​context​</code>​获取​<code>​filtermaps​</code>​。

之前提到过,这是存放filtermap的数组,在 filtermap 中主要存放了 filtername 和 对应的urlpattern。filtetdemo2是我创建的另外一个filter。

Tomcat内存马学习(一)Filter型

继续向下跟踪,这个for循环用来遍历filtermaps中的filtermap,然后找到与当前请求相匹配(urlpattern)的filtermap。进入if判断调用​<code>​findfilterconfig​</code>​方法。找到filtername对应的filterconfig。然后将filterconfig添加到filterchain中

filterconfig 中主要存放 filterdef 和 filter对象等信息。
Tomcat内存马学习(一)Filter型
Tomcat内存马学习(一)Filter型

在addfilter方法中会进行去重操作,并且如果当前数组满了,会自动扩增10个元素。有兴趣可以跟踪进去。

此时filterchain就已经组装好了,接着继续回到​<code>​standardcontextvalue​</code>​中。往下跟踪发现,我的会跟踪到上面那个方框,别人的文章跟踪到下面那个方框,但是两者都会进入​<code>​dofilter​</code>​方法。而且我还不知道第一个方框里是怎么进入该方法的。。先分析第二个方框。跟踪dofilter方法。

Tomcat内存马学习(一)Filter型

进入dofilter方法。发现调用了​<code>​internaldofilter​</code>​方法。跟进​<code>​internaldofilter​</code>​方法

Tomcat内存马学习(一)Filter型

首先会获取​<code>​filterconfig​</code>​对象,然后通过​<code>​filterconfig​</code>​对象获取​<code>​filter​</code>​对象,之后调用了​<code>​filter​</code>​的​<code>​dofilter​</code>​方法。

Tomcat内存马学习(一)Filter型

然后就跳转到我们定义的filter。然后调用​<code>​filterchain​</code>​的​<code>​dofilter​</code>​方法进行往后调用。

Tomcat内存马学习(一)Filter型

这里做一个简单的总结:

通过warpper获取context对象,通过context对象获取filtermaps

遍历filtermaps,匹配urlpattern与当前请求,匹配到的获取其filterconfig。然后将filterconfig添加到filterchain中。返回filterchain

filterchain调用dofilter方法,其中调用​<code>​internaldofilter​</code>​方法获取当前filter的filterconfig。然后从 filterconfig 中获取 filter对象,然后调用 filter 的 dofilter 方法

然后再调用filterchain.dofilter对象

根据上面的简单总结,不难发现最开始是从 context 中获取的 filtermaps,将符合条件的依次按照顺序进行调用,那么我们可以将自己创建的一个 filtermap 然后将其放在 filtermaps 的最前面,这样当 urlpattern 匹配的时候就回去找到对应 filtername 的 filterconfig ,然后添加到 filterchain 中,最终触发我们的内存shell。摘自​<code>​http://wjlshare.com/archives/1529​</code>​。

该方法只能在 tomcat 7.x 以上利用

现在要思考一个问题,如何获取context。

当我们能直接获取 request 的时候,可以通过requests获取servletcontext,强转为standardcontext。

ps:当 web 容器启动的时候会为每个 web 应用都创建一个 servletcontext 对象,代表当前 web 应用

其他获取context的方法我就不细说了。可以看参考文章。

获取到context之后,通过上面的分析会发现。filterconfigs,filterdefs,filtermaps 这三个参数和我们的 filter 有关。需要控制这几个变量。

eval.jsp代码如下

这个方法会直到tomcat重启。

该方法只支持 tomcat 7.x 以上,因为 javax.servlet.dispatchertype 类是servlet 3 以后引入,而 tomcat 7以上才支持 servlet 3

首先访问eval.jsp。

Tomcat内存马学习(一)Filter型

显示注入成功。

然后访问任意url即可进行命令执行。

Tomcat内存马学习(一)Filter型