Spring 严重的漏洞历来都不算多,之前比较严重的那个问题是 Spring 的 JavaBean 的自动绑定功能,导致可以控制 class ,从而导致可以利用某些特性执行任意代码,但是那个漏洞比较鸡肋,不是每次都能触发。
由于 Spring 的框架越来越多,而且后面引入了 SpringEl 作为默认的表达式解析方式,所以一旦引入了类似于 OGNL 的表达式,很可能会带来一些安全问题,本次漏洞就是由于 Spring Web Flow 的数据绑定问题带来的表达式注入,从而导致任意代码执行。
一. 漏洞简介
这个漏洞在今年6月初刚被提交(传送门), 官方并没有详细的信息,通过官方描述和补丁的对比,我们可以大致推断应该是 Spring Web Flow 在 Model 的数据绑定上面,由于没有明确指定相关 model 的具体属性导致从表单可以提交恶意的表达式从而被执行,导致任意代码执行的漏洞,这个漏洞利用除了版本的限制之外还有两个前置条件,这两个前置条件中有一个是默认配置,另外一个就是编码规范了,漏洞能不能利用成功主要就取决于后面的条件。
整体来说这个漏洞危害应该还是有一些的,如果满足2个前置条件,那么直接 RCE 是没什么问题的。在分析这个漏洞之前需要一些 Spring Web flow 的基础知识,给大家推荐这篇文章。
二. 漏洞分析
一开始我也不清楚这个漏洞到底是怎么触发,对于这个漏洞的理解,最好去看下 Spring Web Flow 的教程,搞明白里面的 view-state 是啥,这里不过多对 Spring Web Flow 的基础知识过多解释,那么我们直接看补丁,如下图:
我们发现这里对 addEmptyValueMapping(DefaultMapper mapper, String field, Object model) 这个方法里面表达式解析的实现类进行了替换,直接使用了 BeanWrapperExpressionParser 来解析,关于这个类我们后面再详细说,那么知道触发漏洞的函数后,我们就可以用 Eclipse 或者 Spring Tools 来跟踪下函数调用栈,具体如下:
通过调用关系我们可以发现一共有一下两个函数调用了 addEmptyValueMapping 方法
addDefaultMappings(DefaultMapper mapper, SetparameterNames, Object model)
addModelBindings(DefaultMapper mapper, SetparameterNames, Object model)
这里通过调用关系我们可以大概的搞明白 Spring Web Flow 的执行顺序和流程,由 flowcontroller 决定将请求交给那个 handler 去执行具体的流程,这里我们需要知道当用户请求有视图状态处理时,会决定当前事件下一个执行的流程,同时对于配置文件中我们配置的 view-state 元素,如果我们指定了数据的 model ,那么它会自动进行数据绑定,xml 结构如下(这里以官方的example中的 book 项目为例子)
言归正传,本次漏洞出现的原因就是在 view-state 节点中数据绑定上,我们继续跟踪
addEmptyValueMapping 方法的调用过程,这里通过 eclipse 我们可以发现 bind 方法间接的调用了
addEmptyValueMapping 函数,
到这里我们知道了addEmptyValueMapping 函数存在表达式执行的点,我们现在来详细看下这个
addEmptyValueMapping 函数,如下图
这里我们可以看见,只有控制了 field 参数才能出发漏洞,所以我们重点是找到有没有点我们可以控制从而控制 field 参数来进行任意代码执行,这里明确目标后,我们回过头来看 addDefaultMappings 和
addModelBindings 这两个函数,既然这两个函数都调用了存在缺陷的函数,那么我们看看这两个函数的区别是什么,而且那个函数能能能控制 field 参数,两个函数的区别如下:
这里比较明显的区别就是 addModelBindings 函数中 for (Binding binding : binderConfiguration.getBindings()) 存在这样一个循环,而且就是这个循环的控制决定了 field 参数的值,经过进一步分析,这里控制 field 的参数的决定性因素就是 binderConfiguration 这个变量所控制的值,这里经过源码的跟踪我们可以发现,binderConfiguration 函数的值就是 webflow-*.xml 中 view-state 中 binder 节点的配置,所以这个函数的值来源于配置文件,所以这个函数我们无法控制,从而无法触发漏洞,所以我们重点来看看 addDefaultMappings 这个函数,我们发现 addDefaultMappings 中我们可以控制 field 参数,所以我们重点来看看如何去触发这个函数。
现在我们基本上可以确定了 addDefaultMappings 函数是我们触发漏洞的关键点,那么如上图所示,bing 函数中调用了这两个函数,那么我们可以看出只有当 binderConfiguration 为空的时候才能触发我们的漏洞,那么我们刚才也说了 binderConfiguration 这个值是由配置文件中是否有 binder 节点来控制的(这里需要注意的是程序执行到 bind 方法的前置条件是 view-state 节点中是否配置了 model 属性,即绑定的
javabean 对象是什么),而且 addDefaultMappings 函数中 parameterNames 参数就是我们从表单中传递的值,所以到这里漏洞的触发流程和触发条件基本上清楚了,触发条件如下:
在 webflow 配置文件中 view-state 节点中指定了 model 属性,并且没有指定绑定的参数,即 view-state 中没有配置 binder 节点
而且 MvcViewFactoryCreator 类中 useSpringBeanBinding 默认值(false)未修改
这里为什么一定要 useSpringBeanBinding 的值为 false ,我们来看一下 addEmptyValueMapping 函数,这里的 expressionParser 变量的声明类是 ExpressionParser 接口,那么决定最后 expressionParser.parseExpression(field, parserContext) 这个函数来执行任意表达式是这个变量的赋值,那么在 spring web flow 中这个 expressionParser 的默认值就是 WebFlowELExpressionParser 的实例,这个类表达式默认的解析是有 spel 来执行的,具体可以去跟踪函数,那么在org.springframework.webflow.mvc.builder.MvcViewFactoryCreator.createViewFactory(Expression, ExpressionParser, ConversionService, BinderConfiguration, Validator, ValidationHintResolver)这个类如下图:
我们可以看见如果 useSpringBeanBinding 这个属性为 false 那么久使用默认的解析类,如果这个值为 true 就由 BeanWrapperExpressionParser 这个类来解析,这个类的 parseExpression 函数我们来看看
首先决定了能不能执行的第一个控制变量是 allowDelimitedEvalExpressions ,这个默认值是 false ,所以这里是执行不了表达式的。
所以这里必须满足 useSpringBeanBinding 这个默认值不被改变。
这里需要注意一点,我们构造的恶意参数名称必须以_开头,具体原因看 addDefaultMappings 函数中的
fieldMarkerPrefix 变量。
OK,到这里漏洞的触发条件和流程已经很明确了,下面说说具体怎么利用。
三. 漏洞利用
这次漏洞测试是以 Spring Web flow 官方的 Example 中的例子来进行,因为这里的某个 flow 满足我们的条件,具体配置如下:
项目地址,这里在测试时需要注意修改
org.springframework.webflow.samples.booking.config.WebFlowConfig.mvcViewFactoryCreator()
方法中的改成 factoryCreator.setUseSpringBeanBinding(false); 因为这个工程修改了
useSpringBeanBinding 的默认值。
这里直接到订阅图书,上图说了在 reviewBooking flow 中就能出发,如下图:
点击 confirm ,然后抓包添加恶意参数变量,如下图:
OK,大功告成。
参考资料
帮发小广告:
为了更好的和广大安全爱好者交流,我们搭建了个交流社区,社区主要聚焦在威胁发现以及安全数据分析等领域,我们希望有更多的朋友能加入,能一起分析知识、共同进步。社区地址:https://threathunter.org/,
感谢大家支持。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/322/