天天看點

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型