洞态IAST是一款交互式安全测试工具,关于IAST的介绍可以查看官方文档,本文为使用IAST进行漏洞复现系列的首篇。
文章目录
一、使用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标签来定义表单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" %>
<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会解析表单,对表单中username和password的值进行解析然后展示。其中包括构造ognl表达式%{username}获取用户输入,后端代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Class 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
49public 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
33public 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
3public 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
11public 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方向感兴趣,可以扫描下方二维码关注公众号,获得更及时的内容推送。