阅读: 25
昨天Spring Data Commons爆出远程代码执行漏洞(CVE-2018-1273),攻击者可构造包含有恶意代码的SPEL表达式实现远程代码攻击,直接获取服务器控制权限。绿盟科技发布针对该漏洞的一手分析报告。
文章目录
Pivotal Spring官方发布安全公告,Spring Data Commons组件中存在远程代码执行漏洞(CVE-2018-1273),攻击者可构造包含有恶意代码的SPEL表达式实现远程代码攻击,直接获取服务器控制权限。
Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,包含Commons、Gemfire、JPA、JDBC、MongoDB等模块。此漏洞产生于Spring Data Commons组件,该组件为提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化。请受此漏洞影响用户尽快升级组件。
详情请参考昨天发布的预警通告:
【预警通告】Spring Data Commons远程代码执行漏洞(CVE-2018-1273)
补丁的内容:DATACMNS-1282 – Switched to SimpleEvaluationContext in MapDataBinder.很明显这是一个spel表达式注入漏洞。补丁的内容如下:
补丁大致就是将StandardEvaluationContext替代为SimpleEvaluationContext,由于StandardEvaluationContext权限过大,可以执行任意代码,会被恶意用户利用。
SimpleEvaluationContext的权限则小的多,只支持一些map结构,通用的jang.lang.Runtime,java.lang.ProcessBuilder都已经不再支持,详情可查看SimpleEvaluationContext的实现。
PoC的构造实在可以说路途忐忑,首先官方的描述信息带有一点误导的作用,当然也有可能是我水平不够,没有找到利用点。Spring官方说一个恶意攻击者可以构造任意参数来攻击Spring Data REST支持的http资源或者Spring Data’s projection的请求绑定来达到远程攻击的目的。再加上官方给的链接,直接就盯上了projection这个项目。特别是里面提到的json自动绑定技术。仔细阅读官方资料,发送payload调试,加断点,没有一点反响,肯定是漏洞没触发呗。简单粗暴的方式行不通,只能一个节点一个节点的分析调试。
首先拉取项目https://github.com/spring-projects/spring-data-examples/ ,修改pom.xml中parent节点为
``xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RC1</version> </parent> ```
为什么要降到这个版本,是因为spring-data-commons在2.0.5版本就拒绝了SpEl表达式,添加了如下代码:
``java context.setTypeLocator(typeName -> { throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName); }) ```
初步研究了下,我也绕不过,就只能再降低个版本了。详细release说明见:https://github.com/spring-projects/spring-data-commons/blob/d8a1c2cfde78e87cd4bae3bfcc9782750a783b0b/src/main/resources/changelog.txt
导入到IDEA里,打开项目web[spring-data-web-example]中的UserController.java
``java @Controller @RequiredArgsConstructor @RequestMapping("/users") class UserController { private final UserManagement userManagement; /** * Registers a new {@link User} for the data provided by the given {@link UserForm}. Note, how an interface is used to * bind request parameters. * * @param userForm the request data bound to the {@link UserForm} instance. * @param binding the result of the binding operation. * @param model the Spring MVC {@link Model}. * @return */ @RequestMapping(method = RequestMethod.POST) public Object register(UserForm userForm, BindingResult binding, Model model) { userForm.validate(binding, userManagement); if (binding.hasErrors()) { return "users"; } userManagement.register(new Username(userForm.getUsername()), Password.raw(userForm.getPassword())); RedirectView redirectView = new RedirectView("redirect:/users"); redirectView.setPropagateQueryParams(true); return redirectView; } ```
注意到如下代码:
@RequestMapping(method = RequestMethod.POST) public Object register(UserForm userForm, BindingResult binding, Model model) `
这其中就有UserForm,BindingResult, Model,为啥会是从这个接口进入呢?还是要从问题点出发,也就是MapDataBinder的实现。存在问题的代码是MapDataBinder.setPropertyValue,但是怎么被调用起来的还是挺复杂,慢慢可以看出是ProxyingHandlerMethodArgumentResolver使用了MapDataBinder的接口,实现大体如下:
``java public class ProxyingHandlerMethodArgumentResolver extends ModelAttributeMethodProcessor implements BeanFactoryAware, BeanClassLoaderAware { protected Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { MapDataBinder binder = new MapDataBinder(parameter.getParameterType(), conversionService.getObject()); binder.bind(new MutablePropertyValues(request.getParameterMap())); return proxyFactory.createProjection(parameter.getParameterType(), binder.getTarget()); } ```
这块又是怎么调用起来的,只能找官方文档了,找到了这么一处,http://ju.outofmemory.cn/entry/125029
从 这里可以看出一些端倪,
``java interface Form { @NotBlank String getName(); @NotBlank String getText(); } @Controller @RequestMapping(value = "/guestbook") class GuestbookController { @RequestMapping(method = RequestMethod.GET) String guestbook(Form form, Model model) { … } @RequestMapping(method = RequestMethod.POST) String guestbook(@Valid Form form, Errors errors, Model model) { … } } ```
在处理类似代码的时候会用到 ProxyingHandlerMethodArgumentResolver
,总算上了半点道,这里Form,有Model,对Spring 真是不熟悉,特别是各种各样的注解,看着也头疼,而且不好调试,只能偷懒了,找官方项目,于是找到了web[spring-data-web-example],其实这个早已经在看了,只是前面看了点又放弃了,还好,PoC一发立马弹出计算器。当然这个PoC已经单步调试过,单步调试的坑也是非常多,比如说找可写权限的属性等这里暂且先不说了。只是这次这次直接从web请求触发。具体的栈图如下:
最后给大家放个图,以证明漏洞有效,计算器图:
其实username,password,repeatedPassword字段都可以添加漏洞利用代码。还有一点就是[]是嵌套属性的写法,在[]中间可以写入表达式,先找到username,这个也是跟这个表单属性绑定的,具体的代码如下:
``java interface UserForm { String getUsername(); String getPassword(); String getRepeatedPassword(); ```