想研究記憶體馬還是得學習這方面的知識。對于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即可進行指令執行。