S2-057 技术分析

作者:廖新喜

公众号:廖新喜

Struts2 CVE-2018-11776 S2-057 RCE Ognl

漏洞公告

北京时间8月22日13时,Apache官方发布通告公布了Struts2中一个远程代码执行漏洞(CVE-2018-11776)。该漏洞在两种情况下存在,第一,在xml配置中未设置namespace值,且上层动作配置(upper action(s) configurations)中未设置或用通配符namespace值。第二,使用未设置 value和action值的url标签,且上层动作配置(upper action(s) configurations)中未设置或用通配符namespace值。

补丁对比

如图所示,补丁主要添加了cleanNamespaceName方法,该方法通过白名单的方式来验证namespace是否合法,从官方描述和漏洞修复方式来看,该漏洞应该是一个Ognl的表达式注入漏洞

动态分析

漏洞发布几个小时之后,漏洞发现作者公布了整个发现过程,并且详细分析了一种漏洞情形:https://lgtm.com/blog/apachestrutsCVE-2018-11776 按照该博客的说法,拉取struts2-showcase项目作为示例,修改struts-actionchaining.xml,具体如下:

<struts>
<package name="actionchaining" extends="struts-default" >
<action name="actionChain1" class="org.apache.struts2.showcase.actionchaining.ActionChain1">
<result type="redirectAction">
<param name="actionName">register2</param>
</result>
</action>
</package>
</struts>

在这种情况下,所有到actionChain1.action的请求的返回结果都会指向register2,并且执行链会到ServletActionRedirectResult.execute方法中,具体如下:

从上图可以看出,通过namespace字段,污染了tmpLocation字典,并且设置为了预期的执行的PoC,这也是补丁中为什么要净化namespace的原因,继续跟踪namespace的去向,执行链会到ServletActionRedirectResult的父类的父类StrutsResultSupport.execute方法中,具体如下图

这里有个conditionParse方法,这个方式就是使用Ognl表达式来计算数据值,在系统中用得非常多,而且在一些历史漏洞中,也应该由它来背锅,当然最大的锅还是struts官方,每次漏洞出在哪就修在哪,典型的头痛医头,脚痛医脚。方法实现如下图所示

在这个方法中会使用到TextParseUtil.translateVariables方法,继续跟踪,调用栈进入OgnlTextParser中的evaluate方法,首先会判断传入的表达式是否合法,比如是否能找到${}或者%{}对,接着调用evaluator.evaluate求值,求值过程非常复杂,总得来说就是链式执行过程,具体如下调用栈:

从上图也可以看出最顶层就是通过反射的方式来调用ProcessBuilder的构造函数,中间部分就是链式执行过程中牵涉到一些操作。

我们可以看下求值过程中参数的一些情况。来查看Ognl安全加固的一些变化,具体如下图:

主要是黑名单上又添加了一些类,分别是 classognl.DefaultMemberAccessclasscom.opensymphony.xwork2.ognl.SecurityMemberAccessclassjava.lang.ProcessBuilder

分析就结束了,计算器还是要弹的,如下图:

PoC 构造

这块是最难的,也是最不好调试的,利用showcase项目很早就能执行${(1+1)}=2的效果,但是要弹出计算器,并不容易,其实就是新的沙箱的绕过,当时在调试的时候就发现,每次的返回结果都是空,没办法,只能耐着性子,将原先的PoC进行拆分,一个单元的一个单元的测试。测试获取#context的时候总为空,后来发现导致无法获取OgnlUtil的实例,怎么获取context,有多种方式,从代码结构来看可以从ognl表达式一些固有表达式来获取,如#root,#request等。