CVE-2018-1273 RCE with Spring Data Commons 分析报告

1,漏洞公告:

链接:https://pivotal.io/security/cve-2018-1273
Severity:Critical
Vendor:Spring by Pivotal

Description
Spring Data Commons, versions prior to 1.13 to 1.13.10, 2.0 to 2.0.5, and older unsupported versions, contain a property binder vulnerability caused by improper neutralization of special elements. An unauthenticated remote malicious user (or attacker) can supply specially crafted request parameters against Spring Data REST backed HTTP resources or using Spring Data’s projection-based request payload binding hat can lead to a remote code execution attack.

Affected Pivotal Products and Versions

Severity is critical unless otherwise noted.

  • Spring Data Commons 1.13 to 1.13.10 (Ingalls SR10)
  • Spring Data REST 2.6 to 2.6.10 (Ingalls SR10)
  • Spring Data Commons 2.0 to 2.0.5 (Kay SR5)
  • Spring Data REST 3.0 to 3.0.5 (Kay SR5)
  • Older unsupported versions are also affected

Mitigation
Users of affected versions should apply the following mitigation:

  • 2.0.x users should upgrade to 2.0.6
  • 1.13.x users should upgrade to 1.13.11
  • Older versions should upgrade to a supported branch

Releases that have fixed this issue include:

  • Spring Data REST 2.6.11 (Ingalls SR11)
  • Spring Data REST 3.0.6 (Kay SR6)
  • Spring Boot 1.5.11
  • Spring Boot 2.0.1

There are no other mitigation steps necessary.

Note that the use of authentication and authorization for endpoints, both of which are provided by Spring Security, limits exposure to this vulnerability to authorized users.

Credit
This issue was identified and responsibly reported by Philippe Arteau, GoSecure Inc.

References

2,补丁分析:

补丁的内容:DATACMNS-1282 - Switched to SimpleEvaluationContext in MapDataBinder.很明显这是一个spel表达式注入漏洞。补丁的内容如下:
1
补丁大致就是将StandardEvaluationContext替代为SimpleEvaluationContext,由于StandardEvaluationContext权限过大,可以执行任意代码,会被恶意用户利用。
SimpleEvaluationContext的权限则小的多,只支持一些map结构,通用的jang.lang.Runtime,java.lang.ProcessBuilder都已经不再支持,详情可查看SimpleEvaluationContext的实现。

3,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节点为

1
2
3
4
5
<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表达式,添加了如下代码:

1
2
3
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@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的接口,实现大体如下:

1
2
3
4
5
6
7
8
9
10
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
从 这里可以看出一些端倪,

1
2
3
4
5
6
7
8
9
10
11
12
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请求触发。具体的栈图如下:
2
最后给大家放个图,以证明漏洞有效,计算器图:
3
其实username,password,repeatedPassword字段都可以添加漏洞利用代码。还有一点就是[]是嵌套属性的写法,在[]中间可以写入表达式,先找到username,这个也是跟这个表单属性绑定的,具体的代码如下:

1
2
3
4
interface UserForm {
String getUsername();
String getPassword();
String getRepeatedPassword();