想研究内存马还是得学习这方面的知识。对于tomcat容器的详解参考:
tomca结构
tomcat总体结构如下。
server –> service –> connector & container( engine –> host –> context( wrapper( servlet ) ) )
下面这个图是我对tomcat的理解。如有错误请指出。
这我就说一下我对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>。
负责处理来自相关联的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建议还是先看看。
可以看出,用户的请求会先依次经过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包,方便调试。
即可在external libranes中找到。
现在就来分析一下filter的执行流程。
双击shift键,全局搜索<code>standardwrappervalve</code>类,找到<code>applicationfilterfactory.createfilterchain(request, wrapper, servlet);</code>关键代码。
<code>applicationfilterfactory</code>会创建filterchain(filter链)这个链就是我们经常在filter中使用的filterchain。调用其dofilter方法就会跑到下一个filter。跟进这个<code>createfilterchain</code>方法。
首先通过<code>wrapper</code>调用<code>getparent</code>方法获取当前context。然后通过<code>context</code>获取<code>filtermaps</code>。
之前提到过,这是存放filtermap的数组,在 filtermap 中主要存放了 filtername 和 对应的urlpattern。filtetdemo2是我创建的另外一个filter。
继续向下跟踪,这个for循环用来遍历filtermaps中的filtermap,然后找到与当前请求相匹配(urlpattern)的filtermap。进入if判断调用<code>findfilterconfig</code>方法。找到filtername对应的filterconfig。然后将filterconfig添加到filterchain中
filterconfig 中主要存放 filterdef 和 filter对象等信息。
在addfilter方法中会进行去重操作,并且如果当前数组满了,会自动扩增10个元素。有兴趣可以跟踪进去。
此时filterchain就已经组装好了,接着继续回到<code>standardcontextvalue</code>中。往下跟踪发现,我的会跟踪到上面那个方框,别人的文章跟踪到下面那个方框,但是两者都会进入<code>dofilter</code>方法。而且我还不知道第一个方框里是怎么进入该方法的。。先分析第二个方框。跟踪dofilter方法。
进入dofilter方法。发现调用了<code>internaldofilter</code>方法。跟进<code>internaldofilter</code>方法
首先会获取<code>filterconfig</code>对象,然后通过<code>filterconfig</code>对象获取<code>filter</code>对象,之后调用了<code>filter</code>的<code>dofilter</code>方法。
然后就跳转到我们定义的filter。然后调用<code>filterchain</code>的<code>dofilter</code>方法进行往后调用。
这里做一个简单的总结:
通过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。
显示注入成功。
然后访问任意url即可进行命令执行。