[备份]CVE-2016-4977分析

简介

SpringSecurity是一个流行的权限管理框架,类似Shiro,但功能更加完善。其中OAuth是一个提供安全认证支持的一个模块。用户使用Whitelabel Views处理错误时,攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令

漏洞复现

前往某网站下载Demo代码:http://secalert.net/research/cve-2016-4977.zip 环境搭建不复杂,Maven+SpringBoot项目,直接启动

观察启动文件:resources/application.properties,观察到clientId是acme,密码是password

访问url:http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=hello,输入任意用户名和密码password

img

看到不合法的redirect_uri有显示,是否hello可以换成表达式呢,访问url:http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=${2334-1}

img

进一步测试RCE:http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=${new%20java.lang.ProcessBuilder(new%20java.lang.String(new%20byte[]{99,97,108,99})).start()}

img

漏洞分析

由于程序使用WhiteLabel视图来做返回页面,所以首先分析下面这个文件:org\springframework\security\oauth2\provider\endpoint\WhitelabelErrorEndpoint.java

观察到显示在页面中的errorSummary是oauthError.getSummary(),这里也是程序的关键,加入断点动态调试,访问之前的url,发现表达式被带入了model,根据代码发现model在SpelView中渲染 img

继续跟着程序到SpelView,路径为org/springframework/security/oauth2/provider/endpoint/SpelView.java。Spel的构造方法中定义了一个helper,传入${},定义了一个resolver,做表达式的解析

下方的render方法主要是做页面的渲染,其中用到了helper.replacePlaceholders(template, resolver);,这里的helpers和resolver是构造方法定义的,template是最上方定义的HTML模板

跟入helper.replacePlaceholdersparseStringValue的代码比较复杂。实现的功能是从template中找到helper定义的前缀和后缀,然后交给resolver处理,而resolver的处理逻辑正是上文PlaceholderResolver中的表达式解析

最关键的一步是,parseStringValue方法中存在递归,递归调用导致${xxx${payload}xxx}这样的payload可以被解析到

逻辑上大概是这样:从template中找到${开头并且}结尾的所有部分,第一次取到errorSummary,第二次取到errorSummary里面的表达式,成功操作命令执行,动态调试如下

img

img

补丁分析

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

SpelView构造方法中,加入了一个随机生成的前缀

render方法中,随机前缀拼接到template之前,可以这样理解${errorSummary} -> random{errorSummary},由于没有递归加,所以payload没有加入random,执行前判断random,由于只有最外层符合,所以无法触发RCE

存在暴力破解的可能,因为random固定是六位。但没有价值,因为每执行一条命令都需要几万次的暴力破解请求