洞态IAST-自动检测S2-001

洞态IAST是一款交互式安全测试工具,关于IAST的介绍可以查看官方文档,本文为使用IAST进行漏洞复现系列的首篇。

文章目录

一、使用Dongtai-IAST检测S2-001漏洞

1. 启动在线靶场

登陆在线靶场,启动S2-001环境,等待环境启动之后,点击访问靶场按钮即可前往靶场环境。该环境来源于vulhubvulapps

image-20210416121306566

2. 登录洞态IAST在线网站,查看漏洞检测结果
image-20210416121638264

3. 进入搜索功能,分析完整的污点调用图

3.1 Source点
首先,在org.apache.struts2.dispatcher.mapper.DefaultActionMapper#handleSpecialParameters()方法中,调用Servlet接口的getParameterMap方法获取外部参数,形成初始污点
image-20210416121817595

3.2 Propagator方法

污点经过一系列的处理,最终在com.opensymphony.xwork2.util.OgnlUtil#compile方法中,调用ognl.Ognl#parseExpression方法将污点数据传播为Ognl表达式对象


image-20210416121842241

3.3 Sink方法

最后,在ognl.Ognl#getValue方法中,使用Ognl表达式对象的getValue方法获取Ognl表达式的值

image-20210416121904431

二、S2-001漏洞手工分析
首先,使用struts2标签来定义表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%@ 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 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会解析表单,对表单中usernamepassword的值进行解析然后展示。其中包括构造ognl表达式%{username}获取用户输入,后端代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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方法,代码如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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方法,因此,跟进该方法,代码如下:

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
27
28
29
30
31
32
33
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的值,代码如下:

1
2
3
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对象,代码如下:

1
2
3
4
5
6
7
8
9
10
11
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版本地址:https://iast.huoxian.cn:8001
  • SaaS版本账号申请
  • 洞态IAST合作伙伴计划之整体开源联合开发,申请方式请扫描下方二维码
  • 如需加入技术讨论群,扫描二维码添加微信并备注”洞态IAST-加群”,工作人员将拉您进群

参考链接

进击的DevSecOps,持续分享SAST/IAST/RASP的技术原理及甲方落地实践。如果你对 SAST、IAST、RASP方向感兴趣,可以扫描下方二维码关注公众号,获得更及时的内容推送。

owefsad wechat
进击的DevSecOps,持续分享SAST/IAST/RASP的技术原理及甲方落地实践。如果你对 SAST、IAST、RASP方向感兴趣,可以扫描下方二维码关注公众号,获得更及时的内容推送。
0%