谨防Magic SpEL – Part 2(CVE-2018-1260)

科技动态 2018-06-21

前言

这里我们将对Spring Security OAuth2(CVE-2018-1260)中类似的RCE漏洞进行练习。我们将介绍攻击目标,它的发现方法和利用所需的条件。此漏洞也与2016年披露的另一个漏洞有相似之处。我们将在我们分析修复的部分讨论这种相似性。

分析潜在的漏洞

这一切都始于Find Security Bugs的bug模式SPEL_INJECTION的报告。

它报告SpelExpressionParser.parseExpression()中动态参数的使用,也是我们先前漏洞中使用的相同API。表达式解析器用于解析置于大括号 $ {...} 之间的表达式。

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); //Expression parser
            Object value = expression.getValue(context);
            return value == null ? null : value.toString();
        }
    };
}

控制器类WhitelabelApprovalEndpoint使用此SpelView类构建OAuth2授权流程。SpelView类将名为“template”的字符串(见下面的代码)认定为Spring表达式。

@RequestMapping("/oauth/confirm_access")
public ModelAndView getAccessConfirmation(Map 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); //template variable is a SpEL
}

在方法createTemplate()和createScopes()之后,我们可以看到属性“scopes”被附加到HTML模板中,该模板将被认定为一个表达式。绑定到模板的唯一模型参数是CSRF token。但是,CSRF token不受远程用户的控制。

protected String createTemplate(Map model, HttpServletRequest request) {
   String template = TEMPLATE;
   if (model.containsKey("scopes") || request.getAttribute("scopes") != null) { 
      template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", "");
   }

[...]

private CharSequence createScopes(Map model, HttpServletRequest request) {
   StringBuilder builder = new StringBuilder("
    "); @SuppressWarnings("unchecked") Map scopes = (Map) (model.containsKey("scopes") ? model.get("scopes") : request .getAttribute("scopes")); //Scope attribute loaded here for (String scope : scopes.keySet()) { String approved = "true".equals(scopes.get(scope)) ? " checked" : ""; String denied = !"true".equals(scopes.get(scope)) ? " checked" : ""; String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved) .replace("%denied%", denied); builder.append(value); } builder.append("
"); return builder.toString(); }

此时,我们不确定scopes属性是否可以由远程用户控制。虽然属性(req.getAttribute(..))表示存储在服务器端的会话值,但scope是OAuth2流的可选参数部分。

该参数可以被用户访问,并被保存到服务器端的属性中,最终加载到之前的模板里。

在对文档和一些测试进行了研究之后,我们发现“scopes”是隐式OAuth2流的GET参数部分。因此,隐性模式将是我们易受攻击的应用程序所必需的。

猜想验证和局限性

在测试我们的应用程序时,我们意识到scopes的验证是基于用户定义的scopes白名单的。如果配置了这个白名单,我们就不能随意创造scopes参数。如果scopes没有被定义,则不会对其进行验证。这种局限性可能会使大多数Spring OAuth2应用程序依旧安全。

第一个请求使用scopes ${1338-1} ,见下图。根据回应,我们现在确认scopes参数的值可以被SpelView表达式的执行。

第二个测试是使用表达式 $ {T(java.lang.Runtime).getRuntime().exec(”calc.exe“)} 来验证表达式不限于简单的算术运算。

为了更容易再现,这里是之前截下的http请求原始数据。某些字符(主要是花括号)不受Web容器支持,需要进行URL编码以访问应用程序。

{ ->%7b

POST /oauth/authorize?response_type=code&client_id=client&username=user&password=user&grant_type=password&scope=%24%7bT(java.lang.Runtime).getRuntime().exec(%22calc.exe%22)%7d&redirect_uri=http://csrf.me HTTP/1.1
Host: localhost:8080
Authorization: Bearer 1f5e6d97-7448-4d8d-bb6f-4315706a4e38
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 0

分析修复代码

Pivotal团队选择的解决方案是用简单的视图替换SpelView,并进行基本的级联。

这消除了SpEL执行的所有可能路径。第一个补丁提出了一个潜在的XSS漏洞,但幸运的是,在该漏洞被公开前已经被修复。scopes值现在可以正确转义并且不受任何注入影响。

更重要的是,该解决方案提高了另一个终端的安全性:WhitelabelErrorEndpoint。该终端也不再使用Spel View。它在2016年被发现容易遭受同样的攻击。Spring-OAuth2也使用SpelView类来构建存在漏洞的页面。有趣的是,模板参数是静态的,但绑定到模板的参数是递归执行的。这意味着模型中的任何值都可能导致远程代码执行。

该递归执行通过向表达式边界添加随机前缀来修复。这个模板的安全性现在依赖于6个字符的随机性(6的62次方个可能性)。一些分析师对这种修复方案持怀疑态度,如果做了足够的尝试,就会增加风险。但是,现在这不再是一种可能性,因为SpelView也被从此终端删除。

SpelView类也存在于Spring Boot中。这个实现有一个自定义的解析器来避免递归。这意味着尽管Spring-OAuth2项目不再使用它,但其他组件或专有应用程序可能会重用(复制粘贴)此实用程序类以节省一些时间。出于这个原因,在查找安全漏洞中引入了一种寻找SpelView的新探测器。探测器不会查找特定的包名称,因为我们假定应用程序可能具有SpelView类的副本,而不是对Spring-OAuth2或Spring Boot类的引用。

局限性和利用

我们鼓励您保持所有Web应用程序的依赖项保持最新。如果由于任何原因你必须推迟更新,以下是漏洞具体的利用条件:

1.在依赖关系树中有Spring OAuth2

2.用户必须启用隐式模式,它可以与其他授权类型一起启用

3.scope列表需要为空(未明确设置为一个或多个元素)

好消息是,并非所有OAuth2应用程序都会受到攻击。为了指定任意范围,攻击者的用户配置文件需要有一个空的scope列表。

总结

这是SpEL注入漏洞系列的第二篇也是最后一篇文章。我们希望它为这个不太常见的漏洞类别提供了一些信息。

正如前面在第1部分中提到的,在您自己的应用程序中找到这个漏洞不太可能。它更可能出现类似于Spring-Data或Spring-OAuth的组件。如果您是Java开发人员或负责审计Java代码以确保安全的人员,则可以使用Find Security Bugs(我们用来查找此漏洞的工具)扫描您的应用程序。

参考

https://pivotal.io/security/cve-2018-1260

https://pivotal.io/security/cve-2016-4977

https://docs.spring.io/spring/docs/3.0.x/reference/expressions.html

http://find-sec-bugs.github.io/bugs.htm#SPEL_INJECTION

审核人:yiwang 编辑:边边

安全客

责编内容by:安全客 (源链)。感谢您的支持!

您可能感兴趣的

透过现象看原理:详解 Spring 中 Bean 的 this 调用导致 AOP 失效的原因... 前言 在我们使用Spring时,可能有前辈教导过我们,在bean中不要使用this来调用被@Async、@Transactional、@Cacheab...
CVE-2014-0322 Exploit ActionScript Heap Spray 之前调试CVE-2014-0322这个洞的时候用的是Js的heapspray技术,这几天因为在项目中尝试一点新的利用方法稍微做了点关于ActionS...
Using Spring with App Engine I am struggeling using the basic crud methods with app engine: My code looks l...
Spring Security 与 OAuth2 结合 本文为个人 OAuth2 知识总结,转载请注明出处: https://www.jianshu.com/u/50d83346eaff 摘要:使用OAut...
Angular OIDC OAuth2 client with Google Identity Pl... This article shows how an Angular client could implement a login for a SPA a...