S2-057漏洞原作者自述:如何利用自动化工具发现5个RCE

2018年4月,我向Apache Struts和Struts安然团队中申报了一个新的远程履行代码破绽——CVE-2018-11776(S2-057),在做了某些设置设置设备摆设摆设的办事器上运行Struts,可以经由过程造访精心构造的URL来触发破绽。这一发明是我对Apache Struts的持续安然性钻研的一部分。在这篇文章中,我将先容我发明破绽的历程以及若何使用曩昔的破绽信息来获取Struts内部事情的道理,创建封装Struts特定观点的QL查询。运行这些查询会高亮显示有问题代码的结果。这些工程都托管在GitHub上,后面我们也会向此存储库添加更多查询语句和库,以赞助Struts和其他项目的安然性钻研。

映射进击面

许多安然破绽都涉及了从不受相信的源(例如,用户输入)流向某个特定位置(sink)的数据,并且数据采纳了不安然的处置惩罚要领——例如,SQL查询,反序列化,还有一些其他解释型说话等等,QL可以轻松搜索此类破绽。你只必要描述各类source和sink,然后让DataFlow库完成这些工作。对付特定项目,开始查询造访此类问题的一种好措施是查看旧版本软件的已知破绽。 这可以深入懂得你想要查找的source和sink点。

此次破绽发明历程中,我首先查看了RCE破绽S2-032(CVE-2016-3081),S2-033(CVE-2016-3687)和S2-037(CVE-2016-4438)。 与Struts中的许多其他RCE一样,这些RCE涉及不受相信的输入被转为OGNL表达式,容许进击者在办事器上运行随意率性代码。 这三个破绽分外故意思,它们不仅让我们对Struts的内部事情机制有了一些懂得,而且这三个破绽实际上是一样的,还修复了三回!

这三个问题都是远程输入经由过程变量methodName作为措施的参数通报的造成的OgnlUtil::getValue().

这里proxy有ActionProxy的类型,它是一个接口。 留意它的定义,除了措施getMethod()(在上面的代码顶用于赋值的变量methodName)之外,还有各类措施,如getActionName()和getNamespace()。 这些措施看起来像是会从URL返复书息,以是我就假设所有这些措施都可能返回不受相信的输入。 (后面的文章中,我将深入钻研我对这些输入来自何处的查询造访。)

现在应用QL开始对这些不受相信的源进行建模:

识别OGNL的 sink点

现在我们已经识别并描述了一些不受相信的滥觞,下一步是为sink点做同样的工作。 如前所述,许多Struts RCE涉及将远程输入解析为OGNL表达式。 Struts中有许多函数终极将它们的参数作为OGNL表达式; 对付我们在本文中开始的三个破绽,应用了OgnlUtil :: getValue(),然则在破绽S2-045(CVE-2017-5638)中,应用了TextParseUtil :: translateVariables()。 我们可以探求用于履行OGNL表达式的常用函数,我感到OgnlUtil :: compileAndExecute()和OgnlUtl :: compileAndExecuteMethod()看起来更有戏。

我的描述:

第一次考试测验

现在我们已经在QL中定义了source和sink,我们可以在污点跟踪查询中应用这些定义。 经由过程定义DataFlow设置设置设备摆设摆设来应用DataFlow库:

这里是我应用之前定义的isActionProxySource和isOgnlSink。

留意一下,我这里重载了isAdditionalFlowStep,这样它可以容许我包孕污染数据被传播的额外步骤。 比如容许我将特定于项目的信息合并到流设置设置设备摆设摆设中。 例如,假如我有经由过程某个收集层进行通信的组件,我可以在QL中描述那些各类收集真个代码是什么样的,容许DataFlow库跟踪被污染的数据。

对付此特定查询,我添加了两个额外的流程步骤供DataFlow库应用。 第一个:

它包括跟踪标准Java库调用,字符串操作等的标准QL TaintTracking库步骤。第二个添加是一个近似值,容许我经由过程字段造访跟踪污点数据:

也便是说假如将字段赋了某个受污染的值,那么只要两个表达式都由相同类型的措施调用,对该字段的造访也将被视为污染。看下面的例子:

从上面看出,bar()中this.field的造访可能并不老是受到污染。 例如,假如在bar()之前未调用foo()。 是以,我们不会在默认的DataFlow :: Configuration中包孕这个步骤,由于无法包管数据始终以这种要领流动,然则,对付挖破绽,我感觉加上这个很有用。在后面的帖子中,我将分享一些类似于这个的其他流程步骤,这些步骤对付找bug很有赞助,但因为类似的缘故原由,默认环境下是不包孕这些步骤的。

初始结果和细化查询

我在最新版本的源代码上跑了一下QL,发明因S2-032,S2-033和S2-037仍旧被标记了。 这些破绽明明已经被修复了,为什么照样会报问题呢?

[1] [2] [3]下一页

颠末阐发,我们感觉应该是虽然最初经由过程过滤输入来修复破绽,然则在S2-037之后,Struts团队抉择经由过程调用OgnlUtil :: getMalue()调换对OgnlUtil :: getMalue()的调用来修复它。

callMethod()封装了compileAndExecuteMethod():

compileAndExecuteMethod()在履行之前对表达式履行额外反省:

这意味着我们实际上可以从我们的sink点中删除compileAndExecuteMethod()。

在从新运行查询后,高亮显示对getMethod()作为sink的调用的结果消掉了。 然则,仍旧有一些结果高亮显示了DefaultActionInvocation.java中的代码,这些代码被觉得是固定的,例如对getActionName()的调用,并且数据路径从此处到compileAndExecute()并不是很显着。

路径探索和进一步查询细化

为了搞清楚为什么这个结果被标记,必要能够看到DataFlow库用来孕育发生这个结果的每个步骤。 QL容许编写特殊的路径问题查询,这些查询可天生可逐节点探索的可变长度路径,DataFlow库容许编写输出此数据的查询。

在撰写这篇博客的时刻,LGTM本身没有关于路径问题查询的路径探索UI,是以用了另一个Semmle利用法度榜样:QL for Eclipse。这是一个Eclipse插件,刚好可以满意我们这里的需求,容许完成污点跟踪中的各个步骤。它不仅可以在LGTM.com上对开源项目进行离线阐发,还可以为供给更强大年夜的开拓情况。可以在semmle-security-java目录下的Semmle / SecurityQueries Git存储库中找到以下查询。按照README.md文件中的阐明在Eclipse插件中运行。下文将贴出部分运行的截图。

首先,在initial.ql中运行查询。在QL for Eclipse中,从DefaultActionInvocation.java中选择结果后,您可以在Path Explorer窗口中看到从源到接管器的具体路径。

在上图中可以看出,颠末几个步骤后,调用getActionName()返回的值会流入到pkg.getActionConfigs()返回的工具的get()措施的参数中:

点击下一步,key到了ValueStackShadowMap :: get()措施:

事实证实,由于pkg.getActionConfigs()返回一个Map,而ValueStackShadowMap实现了Map接口,以是理论上pkg.getActionConfigs()返回的值可能是ValueStackShadowMap的一个实例。 是以,QL DataFlow库显示了从变量chainedTo到类ValueStackShadowMap中的get()实现的潜在流程。 实际上,ValueStackShadowMap类属于jasperreports插件,该类的实例仅在几个地方创建。是以我感觉问题应该不在ValueStackShadowMap :: get(),我经由过程在DataFlow :: Configuration中添加一个barrier来扫除这种结果:

这里的意思是假如污染数据流入ValueStackShadowMap的get()或containsKey()措施,那么就不要继承跟踪它。 (我在这里添加了containsKey()措施,由于它也有同样的问题。)

又为ActionMapping :: toString()添加了barrier之后(由于在随意率性工具上调用toString()时出问题),从新运行查询,只留下了部分结果。 当然你也可以考试测验应用Eclipse插件来显示污点路径。

发明破绽

只有10对source和sink,很轻易经由过程手工反省这些是否是真正的问题。 经由过程一些路径,看出有些路径是无效的,以是我又在查询中添加了一些barrier来过滤掉落这些路径。 着末的结果对照故意思。以ServletActionRedirectResult.java中的源代码为例:

在第一步中,调用getNamespace()的source经由过程变量名称空间流入ActionMapping构造函数的参数中:

继承跟这些步骤,看到getUriFromActionMapping()返回一个URL字符串,该字符串应用构造的ActionMapping中的命名空间。 然后经由过程变量tmpLocation流入setLocation()的参数:然后setLocation()在超类StrutsResultSupport中设置location:

然后代码在ServletActionResult上调用execute():

上一页[1] [2] [3]下一页

将location字段通报给对conditionalParse():

conditionalParse()然后将location通报给translateVariables(),它将param转化为引擎盖下的OGNL表达式:

以是看起来当在ServletActionRedirectResult中没有设置namespace参数时,代码从ActionProxy获取命名空间,然后将其作为OGNL表达式。 为了验证这个设法主见,我经由过程以下措施调换了showcase利用法度榜样中的一个设置设置设备摆设摆设文件(例如struts-actionchaining.xml)中的struts标记:

然后我在本地运行showcase利用法度榜样,造访了一个旨在触发此破绽的URL并履行shell敕令以在我的谋略机上打开谋略器利用法度榜样。

弹出谋略器了(中心还花了一些光阴绕过OGNL沙箱)。现在我暂时不供给进一步的细节,后面会发出来。

不单单这一处,来自ActionChainResult,PostbackResult和ServletUrlRenderer的弗成信滥觞都能弹出谋略器! PortletActionRedirectResult中的那个可能也可以,但我没有测试。 四个RCE足以证实问题的严重性。

结论

在这篇文章中,我已经展示了经由过程应用已知(以前)的破绽来赞助构建利用法度榜样的污点模型,然后由 QL DataFlow库找新的破绽。分外是经由过程钻研Struts中之前的三个RCE,终极找到了四个(也可能是五个)!

鉴于S2-032,S2-033和S2-037都是在短光阴内被发明和修复的,安然钻研职员清楚地钻研了S2-032用以探求类似问题并发明S2-033和S2-037。这里就有问题了:我发明的破绽(S2-057)也来自类似的污染源,为什么安然钻研职员和供应商之前没发明?在我看来,这是由于S2-032,S2-033和S2-037之间的相似性在某种意义上是局部的,由于它们都呈现在源代码中的相似位置(整个在Rest插件中)。 S2-057和S2-032之间的相似性处于加倍语义的层面。它们由受污染的源链接,而不是源代码的位置,是以任何能够成功找到这样的变体的软件或对象都必要能够在全部代码库中履行这种语义阐发,就像我现在可演示的QL。

假如你觉得我的这些发明只是命运运限好,由于我假设ActionProxy中的命名空间字段已经被污染了,那么请继承关注下一篇文章,我会展示更多的细节问题,并从传入的HttpRequestServlet本身开始,从“第一原则”开始进行一些污点跟踪。我还将从我的“破绽打猎对象箱”平分享一些对象,以及一些改进查询的一样平常提示。在这样做的历程中,QL还捕获了破绽S2-045!

上一页[1] [2] [3]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包