一、使用Dongtai-IAST检测S2-001漏洞
1. 启动在线靶场
登陆在线靶场,启动S2-001
环境,等待环境启动之后,点击访问靶场按钮即可前往靶场环境。该环境来源于vulhub
和vulapps
2. 登陆洞态IAST
网站,查看漏洞检测结果
3. 进入搜索功能,分析完整的污点调用图
3.1 Source点
首先,在org.apache.struts2.dispatcher.mapper.DefaultActionMapper#handleSpecialParameters()
方法中,调用Servlet
接口的getParameterMap
方法获取外部参数,形成初始污点
3.2 Propagator方法
污点经过一系列的处理,最终在com.opensymphony.xwork2.util.OgnlUtil#compile
方法中,调用ognl.Ognl#parseExpression
方法将污点数据传播为Ognl表达式对象
3.3 Sink方法
最后,在ognl.Ognl#getValue
方法中,使用Ognl表达式对象的getValue
方法获取Ognl表达式的值
二、S2-001漏洞手工分析
首先,使用struts2标签来定义表单
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>S2-001</title>
</head>
<body>
<h2>S2-001 Demo</h2>
<p>link: <a rel="nofollow" href="https://struts.apache.org/docs/s2-001.html">https://struts.apache.org/docs/s2-001.html</a></p>
<s:form action="login">
<s:textfield name="username" label="username" />
<s:textfield name="password" label="password" />
<s:submit></s:submit>
</s:form>
</body>
</html>
当上述表单提交后,struts2会解析表单,对表单中username
和password
的值进行解析然后展示。其中,包括构造ognl
表达式%{username}
获取用户输入,后端代码如下:
Class valueClazz = this.getValueClassType();
if (valueClazz != null) {
if (this.value != null) {
this.addParameter("nameValue", this.findValue(this.value, valueClazz));
} else if (name != null) {
String expr = name;
if (this.altSyntax()) {
expr = "%{" + name + "}";
}
this.addParameter("nameValue", this.findValue(expr, valueClazz));
}
} else if (this.value != null) {
this.addParameter("nameValue", this.findValue(this.value));
} else if (name != null) {
this.addParameter("nameValue", this.findValue(name));
}
在org.apache.struts2.components.UIBean#evaluateParams
方法中,使用Ognl语法拼接参数名称,然后调用findValue
方法,查找ognl表达式的值。findValue
方法最终调用com.opensymphony.xwork2.util.TextParseUtil#translateVariables
方法,代码如下:
public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, TextParseUtil.ParsedValueEvaluator evaluator) {
Object result = expression;
while(true) {
int start = expression.indexOf(open + "{");
int length = expression.length();
int x = start + 2;
int count = 1;
while(start != -1 && x < length && count != 0) {
char c = expression.charAt(x++);
if (c == '{') {
++count;
} else if (c == '}') {
--count;
}
}
int end = x - 1;
if (start == -1 || end == -1 || count != 0) {
return XWorkConverter.getInstance().convertValue(stack.getContext(), result, asType);
}
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 {
result = left + right;
expression = left + right;
}
}
}
在translateVariables
方法中,检查当前expression
是否存在Ognl表达式
,如果存在,循环调用stack.findValue(var, asType)
方法搜索Ognl表达式的结果,并对结果重新构造Ognl表达式,然后循环上述步骤,直到findValue
的结果不存在ognl
表达式,返回数据。上述方法的关键是findValue
方法,因此,跟进该方法,代码如下:
public Object findValue(String expr, Class asType) {
Object var4;
try {
Object value;
if (expr == null) {
value = null;
return value;
}
if (this.overrides != null && this.overrides.containsKey(expr)) {
expr = (String)this.overrides.get(expr);
}
value = OgnlUtil.getValue(expr, this.context, this.root, asType);
if (value != null) {
var4 = value;
return var4;
}
var4 = this.findInContext(expr);
} catch (OgnlException var9) {
var4 = this.findInContext(expr);
return var4;
} catch (Exception var10) {
this.logLookupFailure(expr, var10);
var4 = this.findInContext(expr);
return var4;
} finally {
OgnlContextState.clear(this.context);
}
return var4;
}
上述方法中,先检查是否需要重写ognl表达式,如果需要,替换ognl表达式;然后使用OgnlUtil.getValue
方法获取Ognl表达式expr
的值,代码如下:
public static Object getValue(String name, Map context, Object root, Class resultType) throws OgnlException {
return Ognl.getValue(compile(name), context, root, resultType);
}
上述方法中,首先调用compile
方法解析Ognl表达式的值获取Ognl对象,代码如下:
public static Object compile(String expression) throws OgnlException {
synchronized(expressions) {
Object o = expressions.get(expression);
if (o == null) {
o = Ognl.parseExpression(expression);
expressions.put(expression, o);
}
return o;
}
}
然后调用Ognl.getValue
方法使用Ognl对象和上下文获取表达式的值。至此,触发Ognl表达式解析,且ognl表达式为用户可控。
账号申请
-
SaaS版本地址:http://iast.huoxian.cn:8000/
-
SaaS版本账号申请
-
洞态IAST合作伙伴计划之整体开源联合开发,申请方式请扫描下方二维码
-
如需加入技术讨论群,扫描二维码添加微信并备注"洞态IAST-加群",工作人员将拉您进群