其实写完listener型内存马之后就去看servlet内存马了。但是太复杂,没看懂,护网一开就搁置了。
servlet是什么
servlet就是实际处理业务的代码。像什么登录注册增删改查都是在servlet里写,然后web.xml里绑定一个url。
当然实际业务里都不写web.xml了。看眼自动生成的HelloServlet就知道,都是直接用注解:
1 | package org.example.demo3; |
但是为了方便调试,还是用web.xml(其实没区别,webxml.getServlets().values()都能拿到)
servlet调用过程
要想调用自己的Servlet,就得知道servlet是怎么生成和调用的,在Servlet1的service方法里下断点
往上翻,我们要找servlet是从哪几个变量里取出来构造的,然后修改这几个变量添加自己的servlet
这里有servlet.service,找servlet是哪来的,这是一个私有成员
可以找到是在这赋值的,下个断点,重新访问servlet断到这
往上找,发现是在org/apache/catalina/core/ApplicationFilterFactory.java:83调用的:
这个servlet是调用createFilterChain传进来的,继续往上找这个servlet是哪来的
在org/apache/catalina/core/StandardWrapperValve.java:115
wrapper看起来是和自己写的servlet有关系的,去找这个wrapper是哪来的
wrapper从org/apache/catalina/core/StandardWrapperValve.java:94来的,没有任何参数,进去看看是什么:
直接return了一个成员,正好下面就是这个成员的赋值
打个断点重新访问,没有断到,说明不是每次访问都setContainer,可能只有初始化的时候set一次。这就说明这个wrapper就是存我们自己写的servlet的地方,开始set一次,后面都从这里拿。
停止程序重新启动,发现断到了。这里我又往上找了半天越找越不对劲,后来才发现setContainer不止调用一次,我找的那个根本不是存servlet的wrapper的setContainer
怎么找存servlet的那个setContainer,还记得org/apache/catalina/core/StandardWrapperValve.java:115那里调用了一个allocate吗,进去看看,发现return的instance是一个loadServlet函数返回的
org/apache/catalina/core/StandardWrapper.java:581
进去,发现返回的servlet是用servletClass生成的
这个servletClass是用一个set方法赋值的,断到这正好能看到servletClass的值,这样就能判断是不是自己的servlet
又因为setServletClass是在setContainer之后的,所以只要记一下自己的servlet是在哪出现的,退出重调一次就能找到那个setContainer。
这里调到以后步出一直往下走,最终会发现走到了一个createWrapper里,这就是创建wrapper的地方了:
org/apache/catalina/core/StandardContext.java:2981
createWrapper走完,到上一层,这里可以看到对wrapper进行了很多操作,往下看到org/apache/catalina/startup/ContextConfig.java:1483,这里就是调用setServletClass的地方,所以直接从我们的setServletClass步出也能调到这。没必要从创建wrapper开始找
最后context.addChild(wrapper)添加到了context里,这个context用膝盖想都知道是StandardContext
看看addChild的代码就知道存到了context的children下
下面还有一个addServletMappingDecoded添加了路径映射
构造内存马
剩下的就好说了,照/org/apache/catalina/startup/ContextConfig.java:1460后边的代码写个自己的servlet加到context里就完了。
1 | <%@ page import="java.lang.reflect.Field" %> |
要多加个 wrapper.setServlet(ma);
我不知道为什么,搜索也就在/org/apache/catalina/core/ApplicationContext.java:847有一个调用,但是打断点运行没断到这,说明根本没执行这个setServlet。全局搜索也就/org/apache/catalina/core/ApplicationFilterFactory.java:83有一个,这个地方之前调试走到过,是org.apache.catalina.core.ApplicationFilterChain的setServlet,跟StandardWrapper的setServlet没关系。
进到org.apache.catalina.core.StandardWrapper#setServlet里,发现实际作用是对成员变量instance进行赋值。
搜索其他对instance赋值的操作,发现一个眼熟的,/org/apache/catalina/core/StandardWrapper.java:581。这里也是之前调试走到过的:
576行可以看到,当instance为空的时候,会调用loadServlet给instance赋值,进到loadServlet,可以发现是根据servletClass生成了servlet赋值的
不调用setServlet,第一次访问servlet的时候就会调用loadServlet,而我们内存马的servletClass在在这里是这样的:
org.apache.jsp里肯定没有ma_jsp,甚至org.apache里都没有jsp,这是个虚拟的路径,自然就无法生成servlet,就会报错。
而调用setServlet之后,/org/apache/catalina/core/StandardWrapper.java:576的instance就不是空,就没有第一次访问时生成servlet的这个过程了,就不会报错。