5月12日,Struts官方发布安全公告称,Apache Strut2 REST插件存在漏洞,可以远程执行任意指令,该漏洞编号为S2-033(CVE-2016-3087 ),公告详情如下:
启明星辰ADLab通过分析发现,该漏洞依附于前一段时间闹得沸沸扬扬的S2-032漏洞,Struts官方发布的S2-033安全公告只是针对性地对REST插件功能存在的安全问题进行补充,其漏洞技术成因与S2-032相同。
根据官方的漏洞描述,当开启动态方法调用,并且同时使用了Strut2 REST Plugin插件时,使用“!”操作符调用动态方法可能执行ognl表达式,导致代码执行。
以官方APP包struts2-rest-showcase-280为例,根据漏洞描述,问题出现在RestActionMapper这个类,定位到这个类如下:
其中,getMapping方法用于解析处理REST格式的url。
首先,调用RequestUtils.getUri(request)获取request对象的uri(url除去域名端口之外的链接);然后,uri经过dropExtension、parseNameAndNamespace、handleDynamicMethodInvocation等方法处理,但最关键的是dropExtension、handleDynamicMethodInvocation这两个方法。
dropExtension会循环匹配uri是否以“.json”、“.xml”、“.xhtml”等关键字结尾,如果是的话就将这些关键字去掉,剩下的字符返回给uri。所以利用url必须以”.json”、”.xml”、“.xhtml”等关键字结尾。
然后执行到handleDynamicMethodInvocation这个方法将“!”操作符后边的字符串赋值给actionMethod,如果allowDynamicMethodCalls(是否允许动态方法调用)为true,则将actionMethod添加到mapping的method属性中。
最后,执行到DefaultActionInvocation类的 invokeAction方法时,会取出methodName,由ognlUtil.getValue进行ognl表达式解析。
流程示意图如下所示:
根据上述分析容易构造出如下payload:
http://localhost:8080/struts2-rest-showcase-280/orders!%23_memberAccess%[email protected]@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime%28%29.exec%28%23parameters.command[0]),%23v%3d123,%23v.toString.json?&command=calc.exe
payload经过上述处理后最终的有效method为:
#[email protected]@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime().exec(#parameters.command[0]),#v=123,#v.toString
至于payload 为什么这么写可以参考S2-032的技术分析,原理是一样的。
分析结束,但意外的是实际测试漏洞并未触发。
经过调试发现,Order(action实例)并未进行初始化,导致clientName为null,当程序走到RestWorkflowInterceptor这个过滤器的时候直接return到serverlet输出错误结果。
定位到原因了,只要将Order类实例化即可,修改后的payload如下:
http://localhost:8080/struts2-rest-showcase-280/orders/3!%23_memberAccess%[email protected]@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime%28%29.exec%28%23parameters.command[0]),%23xx%3d123,%23xx.toString.json?&command=calc.exe
上述是根据官方通告的内容定位到漏洞位置并构造出了payload,但到这还没有结束, struts2在相同的位置还存在一个漏洞,并且可无视allowDynamicMethodCalls,就是说在配置了struts.enable.DynamicMethodInvocation为false的情况下该漏洞依然可以触发。相应代码如下图所示:
Strut2 REST Plugin还支持actionName/id/methodName这种方式处理解析uri。直接将id后面的内容作为method属性设置到mapping中,并且未检测struts.enable.DynamicMethodInvocation该属性是否为true,这种方式不再需要使用“!”操作符。
流程图如下所示:
构造的Payload如下:
http://localhost:8080/struts2-rest-showcase-280/orders/3/%23_memberAccess%[email protected]@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime%28%29.exec%28%23parameters.command[0]),%23xx%3d123,%23xx.toString.json?&command=calc.exe
结果如下:
Struts官方已发布升级程序修复该漏洞,建议用户升级至struts 2.3.20.3,2.3.24.3,2.3.28.1版本。但从Struts 2.3.20.3的修复方案看,该版本并没有使用cleanupActionName过滤方法名,而是采用了S2-013的修补方案,将enableEvalExpression设置为false,禁止了ognl表达式执行。不得不说该方案确实简单暴力,至少S2-029、S2-032、S2-033这几个漏洞是不复存在了,但是如果Struts官方哪天不注意又把enableEvalExpression设置成了true,该处就可能再次产生新的利用方式。
http://developer.51cto.com/art/201203/322509.htm
http://seclab.dbappsecurity.com.cn/?p=924
https://cwiki.apache.org/confluence/display/WW/S2-013