转载

S2-057(CVE-2018-11776)流程分析

S2-057的流程分析,该漏洞不好利用,但是整个调用流程是非常值得去研究的,跟一边后基本上对Struts2的执行流程就非常清楚了,关于POC将在后面一篇文章具体说明。

0x00 漏洞描述

Problem It is possible to perform a RCE attack when alwaysSelectFullNamespace is true (either by user or a plugin like Convention Plugin) and then: namespace value isn’t set for a result defined in underlying configurations and in same time, its upper package configuration have no or wildcard namespace and same possibility when using url tag which doesn’t have value and action set and in same time, its upper package configuration have no or wildcard namespace.

S2-057(CVE-2018-11776)流程分析

从漏洞简述中可以看出漏洞在当应用设置 alwaysSelectFullNamespacetrue 时有两种情况可以触发RCE:

  • 在xml配置中未设置 namespace 值,且上层包中未设置 namespace 或其未使用通配符所设置的 namespace
  • 当使用未设置 valueaction 值的 url 标签,且上层包中未设置 namespace 或其未使用通配符所设置的 namespace

当然说啥都不如看diff来的更加直观,最直观的就是在 org.apache.struts2.dispatcher.mapper 中在 DefaultActionMapper 设置 namespace 时增添了对于 namespace 的校验:

S2-057(CVE-2018-11776)流程分析

0x01 调用流程分析

从简述中我们看到比较关键的点是对 setNamespace 所设置的 namespace 进行了一次校验,那么漏洞可能就出现在这个地方。

而让我觉得比较好玩的地方就是我在找 mapping.setNamespace() 的调用栈时也同时找到了整条触发链以及ognl执行的点。我不知道其他的分析是如何在分析之初就可以准确定位到 org.com.opensymphony.xwork2.DefaultActionInvocation$executeResult 的,所以我想通过这一个diff出来的信息来找到整个漏洞的调用过程,以及解释为什么S2-057需要如此苛刻的条件。

漏洞修补的点在 parseNameAndNamespace ,也就是在 mapping.setNamespace() 前首先对 namespace 进行校验,校验这个 namespace 是否在 allowedActionNames 中。那么我们来顺着这个思路看一下 parseNameAndNamespace 的调用栈以及 mapping 的调用关系。

mapping 是一个 ActionMapping 实例,而 parseNameAndNamespace 的作用就是从URI中设置 ActionMappingnamenamespace 。现在知道了这个方法大致的调用栈和作用后,我们看一下哪里调用了这个方法:

S2-057(CVE-2018-11776)流程分析

getMapping() 调用了这个方法,我们跟进一下,看看这个方法干了点什么。

S2-057(CVE-2018-11776)流程分析

可以看到就是根据当前的请求和设置,生成该请求的 mapping ,这个 mapping 可以用来调用相关的 Action ,最后通过 ActionProxy 来根据 struts.xml 找到相应的 Action ,最后再通过 ActionProxy 来创建一个 ActionInvocation 实例来调用相应的拦截器。具体可以参考这张经典的图:

S2-057(CVE-2018-11776)流程分析

ok,回到我们的漏洞,现在也就意味着我们需要跟到Struts2的运行流程中的 ActionMapper 流程这一块,来找哪里调用了 getMapping() 方法。

S2-057(CVE-2018-11776)流程分析

接下来我们看一下 RepareOperationsfindActionMapping 方法,该方法查询并选择性的创建 ActionMapping 是处于主运行流程中的一个方法。

S2-057(CVE-2018-11776)流程分析

可以看到这个方法有一个布尔变量 forceLoopup ,如果为true,则将会从 ActionMapper 实例中查找操作映射,并且该方法最后是返回一个 mapping 的,那么我们继续向上跟,看看哪里调用了此方法。

S2-057(CVE-2018-11776)流程分析

可以看到分别在准备Filter和执行Filter的过程中调用了 findActionMapping() 方法,从整个Struts的流程图中我们可以得知这两个调用点是在 ActionMapper 前后执行的,我们跟进 StrutsExecuteFilter 看一下:

S2-057(CVE-2018-11776)流程分析

可以看到这里校验mapping是否获取成功,如果获取成功,则执行 execute.executeAction() 跟进看一下这个方法具体的执行流:

S2-057(CVE-2018-11776)流程分析

S2-057(CVE-2018-11776)流程分析

可以看到最后将执行到 Dispatcer.serviceAction 中,而这个方法为指定的 Action 生成了 ActionProxy ,也就是Struts2整体运行流程中通过 FilterDispatcher 生成 ActionProxy 的过程。我们具体看一下当mapping中存在 getResult 的结果时,所执行的 proxy.execute() 。首先看一下 execute() 的继承关系:

S2-057(CVE-2018-11776)流程分析

跟进 StrutsActionProxy 看一下具体实现:

S2-057(CVE-2018-11776)流程分析

跟进 invoke() 看一下:

S2-057(CVE-2018-11776)流程分析

我们到 DefaultActionInvocation 来看一下 invoke() 的具体实现,代码很长,这里只截一部分:

S2-057(CVE-2018-11776)流程分析

可以看到如果 ActionInvocation 如果并未执行的话,是通过 executeResult() 来执行的,跟进看一下具体的操作:

S2-057(CVE-2018-11776)流程分析

可以看到这里首先会调用 createResult() 获取相应的result对象,如果result不为null则执行 result.execute(this) ,这里的 execute() 是由具体的result对象实现的。我们看一下 execute() 的继承关系,来看看有多少地方是可以被我们调用的:

S2-057(CVE-2018-11776)流程分析

根据 漏洞发现者的博客 所述以上红框所标注的类都是可以触发漏洞的点,我们选 ServletActionRedirectResult 这个类来看一下:

S2-057(CVE-2018-11776)流程分析

注意到如果namespace不为null时,就会调用 namespace = conditionalParse(namespace, invocation); ,同时这段代码的关键性操作就是调用 setLocation()tmpLocation 进行赋值,然后我们顺着 super 向上看:

S2-057(CVE-2018-11776)流程分析

S2-057(CVE-2018-11776)流程分析

而这里的location就是我们上面 setLocation() 所设置的 tmpLocation ,我们跟进 conditionalParse() 来看一下具体做了什么操作:

S2-057(CVE-2018-11776)流程分析

看到了熟悉的 TextParseUtil.translateVariables ,而这里的 paramtmpLocation ,而 tmpLocation 是通过 getUriFromActionMapping 来设置的,而和 ActionMapping 是有关的,这样一来就绕了回来,也就是说我们如果构造了一个存在ognl表达式的namespace,那么最后这个namespace会被作为参数带入到 TextParseUtil.translateVariables() 中进行ognl表达式解析,并执行表达式。

0x02 利用条件

根据上面对于流程的分析,我们来梳理一下利用条件。

  • 我们需要在namespace中构造污点即包含ognl表达式,这需要我们自定义namespace,而这就需要我们在 default.properties 中设置 struts.mapper.alwaysSelectFullNamespace 为true,默认是为false的。

    S2-057(CVE-2018-11776)流程分析

S2-057(CVE-2018-11776)流程分析

  • 之后为了我们构造的ognl表达式可以执行也就是执行到这四个触发点,我们需要在Struts2执行时让Struts2找到这些触发的action,而这就意味着我们需要修改 struts-actionchaining.xml ,而那里看这几个action的类型呢?—— struts-default.xml (没找到漏洞作者说的Portlet的点):

    S2-057(CVE-2018-11776)流程分析

    也就是说需要根据类型修改 struts-actionchaining.xml ,才能完成action的跳转。

所以这也是为什么这个漏洞利用条件苛刻的原因。

原文  https://lucifaer.com/2018/12/25/S2-057(CVE-2018-11776)流程分析/
正文到此结束
Loading...