转载

【漏洞分析】CVE-2018-1273: RCE with Spring Data Commons 分析报告

【漏洞分析】CVE-2018-1273: RCE with Spring Data Commons 分析报告

阅读: 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表达式注入漏洞。补丁的内容如下:

【漏洞分析】CVE-2018-1273: RCE with Spring Data Commons 分析报告

补丁大致就是将StandardEvaluationContext替代为SimpleEvaluationContext,由于StandardEvaluationContext权限过大,可以执行任意代码,会被恶意用户利用。

SimpleEvaluationContext的权限则小的多,只支持一些map结构,通用的jang.lang.Runtime,java.lang.ProcessBuilder都已经不再支持,详情可查看SimpleEvaluationContext的实现。

PoC构造

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请求触发。具体的栈图如下:

【漏洞分析】CVE-2018-1273: RCE with Spring Data Commons 分析报告

最后给大家放个图,以证明漏洞有效,计算器图:

【漏洞分析】CVE-2018-1273: RCE with Spring Data Commons 分析报告

其实username,password,repeatedPassword字段都可以添加漏洞利用代码。还有一点就是[]是嵌套属性的写法,在[]中间可以写入表达式,先找到username,这个也是跟这个表单属性绑定的,具体的代码如下:

``java
interface UserForm {
String getUsername();
String getPassword();
String getRepeatedPassword();
```
原文  http://blog.nsfocus.net/cve-2018-1273-analysis/
正文到此结束
Loading...