Spring Security OAuth 是为 Spring 框架提供安全认证支持的一个模块,在7月5日其维护者发布了这样一个升级公告,主要说明在用户使用Whitelabel views来处理错误时,攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。漏洞的发现者在10月13日公开了该漏洞的挖掘记录。
授权状态下远程命令执行
2.0.0 to 2.0.9
1.0.0 to 1.0.5
docker pull maven
FROM maven
WORKDIR /tmp/
RUN wget http://secalert.net/research/cve-2016-4977.zip
RUN unzip cve-2016-4977.zip
RUN mv spring-oauth2-sec-bug/* /usr/src/mymaven
WORKDIR /usr/src/mymaven
RUN mvn clean install
CMD ["java", "-jar", "./target/demo-0.0.1-SNAPSHOT.jar"]
docker build -t mvn-spring . docker run --rm --name mvn-spring-app -p 8080:8080 mvn-spring
2.漏洞分析
首先我们查看src/resources/application.properties的内容来获取clientid和用户的密码:
接着我们访问这个url:
http://localhost:8080/oauth/authorize?responsetype=token&clientid=acme&redirect_uri=hellotom
其中client_id就是我们前面获取到的,然后输入任意用户名,密码填上面的password。
点击登录后程序会返回这样一个页面:
可以看到由于hellotom对于redirect_uri来说是不合法的值,所以程序会将错误信息返回并且其中带着hellotom,那么这个不合法的值可不可以是一个表达式呢?我们再访问这个url:
http://localhost:8080/oauth/authorize?responsetype=token&clientid=acme&redirect_uri=${2334-1}
结果如下:
可以看到表达式被执行,触发了漏洞。
下面看代码,由于程序使用Whitelabel作为视图来返回错误页面,所以先看/spring-security-oauth/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/WhitelabelErrorEndpoint.java中第18-40行:
@FrameworkEndpoint
public class WhitelabelErrorEndpoint {
private static final String ERROR = "<html><body><h1>OAuth Error</h1><p>${errorSummary}</p></body></html>";
@RequestMapping("/oauth/error")
public ModelAndView handleError(HttpServletRequest request) {
Map<String, Object> model = new HashMap<String, Object>();
Object error = request.getAttribute("error");
// The error summary may contain malicious user input,
// it needs to be escaped to prevent XSS
String errorSummary;
if (error instanceof OAuth2Exception) {
OAuth2Exception oauthError = (OAuth2Exception) error;
errorSummary = HtmlUtils.htmlEscape(oauthError.getSummary());
}
else {
errorSummary = "Unknown error";
}
model.put("errorSummary", errorSummary);
return new ModelAndView(new SpelView(ERROR), model);
}
}
这里定义了Whitelabel对错误的处理方法,可以看到程序通过oauthError.getSummary()来获取错误信息,我们再次访问这个 url 并开启动态调试:
http://localhost:8080/oauth/authorize?response_type=token&client_id=acme&redirect_uri=${2334-1}
请求中的${2334-1}已经被带入了errorSummary中,然后errorSummary被装入model中,再用SpelView进行渲染。
我们跟进SpelView到spring-security-oauth/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/SpelView.java中第21-54行:
class SpelView implements View {
...
public SpelView(String template) {
this.template = template;
this.context.addPropertyAccessor(new MapAccessor());
this.helper = new PropertyPlaceholderHelper("${", "}");
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();
}
};
}
...
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
...
String result = helper.replacePlaceholders(template, resolver);
...
}
}
可以看到在render时通过helper取${}中的值作为表达式,再用parser.parseExpression来执行,跟进一下replacePlaceholders这个函数,在/org/springframework/util/PropertyPlaceholderHelper.class第47-56行:
public String replacePlaceholders(String value, final Properties properties) {
Assert.notNull(properties, "\'properties\' must not be null");
return this.replacePlaceholders(value, new PropertyPlaceholderHelper.PlaceholderResolver() {
public String resolvePlaceholder(String placeholderName) {
return properties.getProperty(placeholderName);
}
});
}
这个函数是个递归,也就是说如果表达式的值中有${xxx}这样形式的字符串存在,就会再取xxx作为表达式来执行。
我们看动态调试的结果:
首先因为传入了${errorSummary},取errorSummary作为表达式来执行,继续执行程序:
由于errorSummary中存在${2334-1},所以又取出了2334-1作为表达式来执行,从而触发了漏洞。所以从这里可以看出,漏洞的关键点在于这个对表达式的递归处理使我们可控的部分也会被当作表达式执行。
可以看到在第一次执行表达式之前程序将$替换成了由RandomValueStringGenerator().generate()生成的随机字符串,也就是${errorSummary} -> random{errorSummary},但是这个替换不是递归的,所以${2334-1}并没有变。
然后创建了一个helper使程序取random{}中的内容作为表达式,这样就使得errorSummary被作为表达式执行了,而${2334-1}因为不符合random{}这个形式所以没有被当作表达式,从而也就没有办法被执行了。
不过这个Patch有一个缺点:RandomValueStringGenerator生成的字符串虽然内容随机,但长度固定为6,所以存在暴力破解的可能性。
原文来自:知道创宇
声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com
支持全球约2.4万个城市地区天气查询,如:天气实况、逐日天气预报、24小时历史天气等
支持识别各类商场、超市及药店的购物小票,包括店名、单号、总金额、消费时间、明细商品名称、单价、数量、金额等信息,可用于商品售卖信息统计、购物中心用户积分兑换及企业内部报销等场景
涉农贷款地址识别,支持对私和对公两种方式。输入地址的行政区划越完整,识别准确度越高。
根据给定的手机号、姓名、身份证、人像图片核验是否一致
通过企业关键词查询企业涉讼详情,如裁判文书、开庭公告、执行公告、失信公告、案件流程等等。