您的位置 » 首页 » 代码审计,渗透测试 » 漏洞分析:Struts2(s2-016)远程代码执行漏洞详细代码分析

漏洞分析:Struts2(s2-016)远程代码执行漏洞详细代码分析

发表于4年前 | 作者: seay | 分类: 代码审计, 渗透测试 | 孵化于:2013年07月22日 | 文章热度:9,644 次 全屏阅读

显示不全请点击全屏阅读

之前winwin已经发过这个漏洞的分析文章,分析的很到位,不过有几个点有些问题,所以我在这里把自己的分析内容发出来,供各位参考。

      这个漏洞的数据污染点和触发点,和其他的Struts不一样,所以本篇分析将从Struts执行流程中剖析此漏洞。在Struts2.3以后,官方将原有的起始过滤器换为:org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.class
      所以我们此次跟踪的第一个断点便下在这个类的doFilter方法中,代码如下:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {



        HttpServletRequest request = (HttpServletRequest) req;

        HttpServletResponse response = (HttpServletResponse) res;



        try {

            prepare.setEncodingAndLocale(request, response);

            prepare.createActionContext(request, response);

            prepare.assignDispatcherToThread();

            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {

                chain.doFilter(request, response);

            } else {

                request = prepare.wrapRequest(request);

                [color=Red]ActionMapping mapping = prepare.findActionMapping(request, response, true);[/color]

                if (mapping == null) {

                    boolean handled = execute.executeStaticResourceRequest(request, response);

                    if (!handled) {

                        chain.doFilter(request, response);

                    }

                } else {

                    execute.executeAction(request, response, mapping);

                }

            }

        } finally {

            prepare.cleanupRequest(request);

        }

    }

 

通过红色代码获取当前访问的action映射,下面我们来看PrepareOperations类中的findActionMapping方法:

 

public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {

        ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);

        if (mapping == null || forceLookup) {

            try {

                [color=Red]mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());[/color]

                if (mapping != null) {

                    request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);

                }

            } catch (Exception ex) {

                dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);

            }

        }



        return mapping;

    }

 

跟进至红色代码处,这句代码通过调用DefaultActionMapper类的getMapping方法来获取action映射,我们继续跟进到这个方法:

 

public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {

        ActionMapping mapping = new ActionMapping();

        String uri = getUri(request);



        int indexOfSemicolon = uri.indexOf(";");

        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;



        uri = dropExtension(uri, mapping);

        if (uri == null) {

            return null;

        }



        parseNameAndNamespace(uri, mapping, configManager);

             handleSpecialParameters(request, mapping);

        return parseActionName(mapping);

    }

 

红色代码部分是用于处理特殊参数,本漏洞的触发点redirect就属于它处理的内容,下面我们来看下它的代码:

 

public void handleSpecialParameters(HttpServletRequest request, ActionMapping mapping) {

        // handle special parameter prefixes.

        Set<String> uniqueParameters = new HashSet<String>();

        Map parameterMap = request.getParameterMap();

        for (Object o : parameterMap.keySet()) {

            String key = (String) o;



            // Strip off the image button location info, if found

            if (key.endsWith(".x") || key.endsWith(".y")) {

                key = key.substring(0, key.length() - 2);

            }



            // Ensure a parameter doesn't get processed twice

            if (!uniqueParameters.contains(key)) {

                ParameterAction parameterAction = (ParameterAction) prefixTrie.get(key);

                if (parameterAction != null) {

[color=Red]                    parameterAction.execute(key, mapping);[/color]

                    uniqueParameters.add(key);

                    break;

                }

            }

        }

 

继续跟着流程走,在红色代码段之前的操作是从用户传入的参数中提取特殊参数,红色代码则是针对这个参数进行处理的地方。DefaultActionMapper类针对四种不同的特殊参数,分别定义了不同的execute方法,这里我们只看处理redirect参数的方法,看下它的代码:

 

public void execute(String key, ActionMapping mapping) {

                        ServletRedirectResult redirect = new ServletRedirectResult();

                        container.inject(redirect);

                        redirect.setLocation(key.substring(REDIRECT_PREFIX

                                .length()));

                        mapping.setResult(redirect);

                    }

 

代码比较简单,就是新建一个ServletRedirectResult对象,将用户输入的参数值插入到它的Location属性中,最后将这个对象覆盖掉映射的result属性。
      问题的关键点就在这个result属性中,Struts在处理action后,返回的内容都是依赖result属性中的内容。平常这个属性都是通过配置文件来设置的,但是在这里,用户可以通过redirect来控制这个属性的内容。而且,用来解析返回内容的conditionalParse方法使用了translateVariables方法处理参数,这个方法会将其参数作为Ognl表达式执行,从而导致此漏洞的触发。conditionalParse方法代码如下:

 

protected String conditionalParse(String param, ActionInvocation invocation) {

        if (parse && param != null && invocation != null) {

            return TextParseUtil.translateVariables(param, invocation.getStack(),

                    new TextParseUtil.ParsedValueEvaluator() {

                        public Object evaluate(String parsedValue) {

                            if (encode) {

                                if (parsedValue != null) {

                                    try {

                                        // use UTF-8 as this is the recommended encoding by W3C to

                                        // avoid incompatibilities.

                                        return URLEncoder.encode(parsedValue, "UTF-8");

                                    }

                                    catch(UnsupportedEncodingException e) {

                                        if (LOG.isWarnEnabled()) {

                                            LOG.warn("error while trying to encode ["+parsedValue+"]", e);

                                        }

                                    }

                                }

                            }

                            return parsedValue;

                        }

            });

        } else {

            return param;

        }

    }

差不多就这些,应该够详细了。利用方法就不说了,铺天盖地都是,实在不行,随便搞个利用工具抓下包。想看原理的话,90sec上倒是有个人分析的不错,推荐一看。

作者:唐门三少 

 

Tags:

Struts2漏洞,

如果您喜欢我的博客,欢迎点击图片定订阅到邮箱填写您的邮件地址,订阅我们的精彩内容: 也可以点击链接【订阅到鲜果】

如果我的想法或工具帮助到了你,也可微信扫下方二维码打赏本人一杯咖啡


来自 Seay互联网安全博客
本文地址:http://www.cnseay.com/3213/
文章版权说明请看置顶文章,尊重作者,转载请以链接形式标明原文地址

马上分享给你的朋友吧~

已经有5个筒子的人留下了脚印...

  • 莫小年 说:
    1楼
    2013 年 7 月 22 日 上午 7:29 回复

    博主,问你个问题,我基础不好,msf,C,C++,python,XSS,Linux等等都不会。。。。。。有点乱,我也不知道从哪里开始。。。。。
    能解答下吗

    • 【管理员】seay 说:
      2013 年 7 月 22 日 上午 8:34 回复

      喜欢什么就玩什么呗,兴趣是第一位

      • 莫小年 说:
        2013 年 7 月 22 日 上午 8:38 回复

        我喜欢渗透测试啊,但是渗透的时候需要用到很多东西啊,XSS,msf,back track什么的,比如扫描啊什么的,需要python的脚本啊,所以我就有点乱了,不知道从哪里下手了。。。。。。。

        • 【管理员】seay 说:
          2013 年 7 月 22 日 下午 12:22 回复

          不急,慢慢来

  • shack2 说:
    2楼
    2013 年 7 月 25 日 上午 10:07 回复

    编程是一门思想,程序语言之间是相同的,学好一门语言,其他的语言你就能明白了,建议学一门面向对象的语言,这其中建议学习Java或c#比较简单,或者学习php,个人建议前两种

发表评论

你的大名(必填)

你的邮箱(必填)

评论内容(必填)