漏洞前提
开启动态方法调用, struts.xml配置
<constant name="struts.enable.DynamicMethodInvocation" value="true" />
沙盒绕过
通过ognl表达式静态调用获取ognl.OgnlContext的DEFAULT_MEMBER_ACCESS属性,并将获取的结果覆盖_memberAccess属性,这样就可以绕过SecurityMemberAccess的限制。
漏洞详情
https://struts.apache.org/docs/s2-032.html
详解测试
假如动态方法调用已经开启,然后我们要调用对应的login方法的话 我们可以通过http://localhost:8080/struts241/index!login.action来执行动态的方法调用。这种动态方法调用的时候method中的特殊字符都会被替换成空,但是可以通过http://localhost:8080/struts241/index.action?method:login来绕过无法传入特殊字符的限制。
接收到的参数会经过处理存入到ActionMapping的method属性中。DefaultActionProxyFactory将ActionMappping的method属性设置到ActionProxy中的method属性(虽然做了escapeEcmaScript,escapeHtml4过滤,但是我们可以通过变量传递方式绕过,具体可以参考poc)。如下图
而DefaultActionInvocation.java中会把ActionProxy中的method属性取出来放入到ognlUtil.getValue(methodName + “()”, getStack().getContext(), action);方法中执行ognl表达式,如下图
3. 沙盒绕过
通过ognl表达式静态调用获取ognl.OgnlContext的DEFAULT_MEMBER_ACCESS属性,并将获取的结果覆盖_memberAccess属性,这样就可以绕过SecurityMemberAccess的限制。
4. poc
a. 测试环境
b. 结果
修复方案
升级至struts 2.3.20.2,struts 2.3.24.2, struts 2.3.28.1
临时解决方案
1. 检查是否使用struts2
2. 检查是否开启动态方法调用
3. 在struts前端nginx配置正则拦截攻击请求
正则:if($args ~ @ognl.OgnlContext@DEFAULT_MEMBER_ACCESS) { return 404;}
升级Struts 2至Struts 2.3.20.2, Struts 2.3.24.2 或者 Struts 2.3.28.1,以便彻底解决此问题。