什么是STRUTS 2漏洞?
Struts 是Apache软件基金会(ASF)赞助的一个开源项目,通过采用JavaServlet/JSP技术,实现基于Java EEWeb应用的MVC设计模式的应用框架,Struts 2是Struts的下一代产品,是在 Struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。
Struts2框架广泛应用于政府、公安、交通、金融行业和运营商的网站建设,作为网站开发的底层模板使用。Struts2漏洞,主要指的是J2EE开源框架struts2出现的命令执行漏洞,危害巨大,可导致远程执行任意系统命令,进而获取系统控制权,数据库控制权,导致信息泄露。所有使用struts2框架开发的系统都会受到影响。
STRUTS 2攻防对抗历史简要回顾。
Struts2的代码执行问题最早要追溯到2010年,当时来自Google Security Team的Meder Kydyraliev发现可以通过用unicode编码的形式绕过参数拦截器对特殊字符“#”的过滤,造成代码执行问题,官方漏洞编号S2-003,这也是最早的存在记录的一个struts2远程代码执行漏洞。对于第一个出现的struts2远程代码执行漏洞官方当时并没有意识到这已经打开了潘多拉的魔盒,通过传递非法参数绕过过滤调用OGNL表达式,这也就是后来多数的struts远程代码执行漏洞的利用流程。对于S2-003官方只是简单的用正则将含有”u0023”的请求全部过滤掉,由于”u0023”在传递的过程中被转义为”\u0023”所以正则根本没有匹配上。导致漏洞的第一次修补实际上失败了。
OGNL表达式可以调用java的静态方法,开发者后来也意识到命令执行的危害。后来OGNL上下文中一些命名空间中的属性,比如将#_memberAccess.allowStaticMethodAccess设置为true,#context["xwork.MethodAccessor.denyMethodExecution"]设置为false。但是通过unicde编码绕过过滤规则的问题依然存在。比如将”u0023”换成八进制的“43”,即可绕过官方当时的修复。
后来官方终于意识到了问题的严重性在过滤的时候更加严谨的改写了正则,过滤掉了出现\, @等字符的请求内容,官方修改的正则表达式,如图所示。
修补后的正则虽然更为严谨但是问题依然存在。大概是在11年Google Security Team的一位成员又提出了新的利用思路 (CVE-2011-3923),借助action实例中的私有变量的set方法执行OGNL调用java静态方法执行任意命令。对于这个CVE的修复官方依然是通过正则过滤的方式来修复此问题,官方修复如图所示。
由于struts2框架底层是利用OGNL表达式实现的。官方为了防止在OGNL表达式中直接调用java静态方法,它在OGNL上下文中内置了几个命名对象。例如,#_memberAccess["allowStaticMethodAccess"]默认被设置为false,#context["xwork.MethodAccessor.denyMethodExecution"]默认被设置为true。
但是上面提到的这几个属性的值可以利用执行OGNL进行修改,修改相关属性之后,又可以直接调用java静态方法。
- 13年的时候,在S2-013修补中,#_memberAccess["allowStaticMethodAccess"]的属性,被设置为没有权限被修改。这样看似从根本上解决了问题。但是利用java反射类来访问私有成员变量的方式依然可以绕过达到直接修改前面那两个属性。
- 此外java.lang.ProcessBuilder这个类,new一个实例然后调用start()方法,便达到命令执行的目的。也可以绕过apache设置的限制。
- 此后出现了S2-016,这个漏洞是DefaultActionMapper类支持以”action:”、”redirect:”、”redirectAction:”作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,又导致命令执行。
此后官方对于S2-020的修复依然是头痛医头脚痛医脚,使用正则表达式来过滤用户请求。依然导致了大量的绕过。
Struts2历届的漏洞补丁页面可以从这个页面查看到。
https://struts.apache.org/docs/security-bulletins.html
s2-032漏洞技术分析
此次漏洞存在于struts2的动态方法引用功能。只要在struts2配置文件中开启该功能,就可能被利用。
<constant name=”struts.enable.DynamicMethodInvocation” value=”true” />
如果我们请求http://localhost/index.action?method:OGNL的情况下,请求的OGNL表达式会被执行,造成命令执行。
method后面跟的方法名会被struts2进行解析,代码位于DefaultActionMapper.java中。
可以看到
mapping.setMethod(key.substring(METHOD_PREFIX.length()));
将我们传入的方法加入到map中。然后在DefaultActionInvocation.java中,被invokeAction引用。
invokeAction首先获取方法字符串,然后调用ognlUtil.getValue来执行方法并获取方法结果。问题就出在传给ognl的methodName没有进行严格的过滤,尤其是没有过滤ognl关键字,从而造成命令执行。
通过对比2.3.28和2.3.28.1,可以发现修补的位置:
在进行方法添加的时候,对传进来的方法进行了过滤,调用了cleanupActionName。cleanupActionName的定义如下:
其中allowdActionNames定义是
protected Pattern allowedActionNames = Pattern.compile(“[a-zA-Z0-9._!/\-]*”);
所以代码的意思是去除一切不在上述范围的字符。
最后的漏洞利用exp格式举例
还可以用如下的脚本代码进行网址漏洞的批量检测。