*本文中涉及到的相关漏洞已报送厂商并得到修复,本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。
台湾白帽Orange Tsai(蔡政达)受邀前往本届 Black Hat USA 和 DEFCON 26发表议题演讲,在《Breaking Parser Logic! Take Your Path Normalization Off and Pop 0days Out》的演讲中,他分享了如何基于“不一致性”安全问题,综合利用4个功能性Bug,实现对亚马逊(Amazon)协同平台系统的远程代码执行。
以下是他的详细技术分享:
背景说明
在过去两年时间里,我重点在研究一些“不一致(inconsistency)”的安全问题,这是什么问题呢?这就有点类似我去年在 Black Hat 的演讲以及《GitHub SSRF to RCE》的研究一样,我先通过发现URL解析器和URL获取器之间的不一致问题,形成了整体SSRF绕过,最终实现更严重的漏洞利用。
另外,这篇由@0x09AL写的文章《Bypassing Web-Application Firewalls by abusing SSL/TLS》,也详细阐述了 “不一致“ 安全问题导致的重要漏洞,值得拜读。
有了之前的基础,今年我着重研究了路径解析器和规范化之间的不一致安全问题。理论上来说,因为不同对象实体具备不同的标准和实现需求,所以很难开发出一款设计严格而全面的解析器。但当解析器出现安全Bug时,为了不影响业务逻辑,研发上通常的做法是采用某种替代方法或是增加某种过滤器,而不是直接给Bug打补丁,最后的影响是治标不治本。所以,这样一来,如果过滤器和调用方法之间存在任何不一致问题,就可能轻松绕过系统本身设置的安全机制。
当我在阅读一些漏洞分析报告时,我注意到了一种叫”URL路径参数“( URL Path Parameter)的功能特性。一些研究人员已经指出,如果编程出现错误,这种特性可能会导致安全问题。通过点点滴滴的关联分析,我发现这种特性可以完美地应用在多层体系结构中,而且默认情况下,不必编码出错,就存在攻击面,可导致漏洞利用。如果你在反向代理中使用了Java后端服务,那么就可能存在这种漏洞!
早先在2015年时,我首先是在一次红队测试中发现了这种攻击面,之后,我觉得这个问题威力超强,也想看看安全圈内的知悉面,于是,我就在WCTF 2016比赛的自出题中设计了一道相关题目。
WCTF是由Belluminar和360共同举办的比赛,这与其他CTF比赛中的解题模式(Jeopardy)和攻防模式(Attack-Defense)不一样,它邀请了全球各国的前10名团队,每个团队都需要设计两道挑战题目,所以总共有20个挑战题目。你解题数量越多,你得到的点数就越多。然而,最后却没人能解出我出的这道题目。所以,那时我认为这种技术可能还并不为大多数人知晓。另外,我也对DirBuster、wFuzz、DirB 和 DirSearch这些扫描器作了测试,但只有DirSearch在2017年5月加入了这种扫描规则。
因此,今年我打算分享这个议题。但为了说服Black Hat 的审查委员会,我需要有力的用例支撑。所以,我又重拾挖洞,然而在测试中我发现,这种攻击面不仅可以造成敏感信息泄露,还能绕过访问控制列表(像我发现的这个优步OneLogin登录绕过漏洞),在某些漏洞众测项目中还能导致远程代码执行(RCE)。在这篇文章中,我就来介绍,利用这种 "不一致" 攻击面问题,综合4个功能Bug,实现对亚马逊协同平台的远程代码执行(RCE)。
多层架构的不一致性,可以形象的用以下图片来表示:
前言
首先,感谢亚马逊(Amazon)开放的漏洞披露策略,与亚马逊安全团队Nuxeo的合作非常顺畅,仅从漏洞上报进程来看,就可以看出亚马逊快速的漏洞响应速度,以及他们积极的应对措施。
开始我们要从网站 corp.amazon.com 说起,这貌似是亚马逊的一个内部协同系统,从网站的底部版权信息来看,该系统由开源项目Nuxeo部署构建。而Nuxeo又是一个庞大的Java项目,且刚开始我只是想提高一下我的Java审计技术。所以故事就从这里说起吧!
四个漏洞(Bug)
对我来说,当我拿到Java源码时,我首先会看看 pom.xml 配置文件,然后再去查找是否存在过期的引用包。在Java生态系统中,很多漏洞都像 OWASP Top 10 - A9 描述的组件漏洞那样,如涉及Struts2,、FastJSON、XStream等反序列化组件时,就可能存在漏洞
pom.xml主要描述了项目的maven坐标,依赖关系,开发者需要遵循的规则,缺陷管理系统,组织和licenses,以及其他所有的项目相关因素,是项目级别的配置文件。
这里的Nuxeo项目中,初看貌似其中的包都是最新的,但我却发现了一个”老朋友“ - Seam框架。Seam是基于 JBoss 的web框架,隶属红帽Linux系统的分支,在早前几年非常流行,但现在仍然存在大量基于Seam的web应用。
我曾在2016年对Seam进行过审计,也发现了其中一些风险隐患,但最终在这里,貌似也无法直接照搬实现。我们先继续往下分析。
BUG 01:路径规范化错误导致的访问控制列表(ACL)绕过
当从WEB-INF/web.xml文件中查看访问策略时,我发现Nuxeo项目使用了一个通用的验证过滤器NuxeoAuthenticationFilter,并把/*类型目录映射到了这个过滤器上。这种验证机制下,大部份网页都需要进行验证,但也存在一个包含login.jsp这样页面的访问入口白名单,所有这些功能都由一个名为bypassAuth的方法来具体实现:
protected boolean bypassAuth(HttpServletRequest httpRequest) {
// init unAuthenticatedURLPrefix
try {
unAuthenticatedURLPrefixLock.readLock().lock();
String requestPage = getRequestedPage(httpRequest);
for (String prefix : unAuthenticatedURLPrefix) {
if (requestPage.startsWith(prefix)) {
return true;
}
}
} finally {
unAuthenticatedURLPrefixLock.readLock().unlock();
}
// ...
return false;
}
从上述代码可知,bypassAuth方法会检索当前请求页面与unAuthenticatedURLPrefix作一个比较,但是,bypassAuth方法是如何去检索当前请求页面的呢?为此,Nuxeo写了一个getRequestedPage方法来从HttpServletRequest.RequestURI中提取出当前的请求页面,这样一来,第一个问题就出在这里了!
protected static String getRequestedPage(HttpServletRequest httpRequest) {
String requestURI = httpRequest.getRequestURI();
String context = httpRequest.getContextPath() + '/';
String requestedPage = requestURI.substring(context.length());
int i = requestedPage.indexOf(';');
return i == -1 ? requestedPage : requestedPage.substring(0, i);
}
为了去处理URL路径参数,Nuxeo用分号对所有的尾部进行了截断,但是,URL路径参数的行为是各式各样的,每个web服务器都有自己的实现方式,Nuxeo的这种处理方式在WildFly、JBoss 和 WebLogic中可能会很安全,但在这里的Tomcat下可能就有问题了。也就是,getRequestedPage方法和 Servlet 容器之间的差异导致的安全问题!
由于它的截断机制,我们就能伪造一个与访问控制列表(ACL)白名单匹配,但又是Servlet 容器中未经授权的请求。这里,我们选择login.jsp作为前缀请求文件,访问控制列表(ACL)绕过的请求如下:
$ curl -I https://collaborate-corp.amazon.com/nuxeo/[unauthorized_area]
HTTP/1.1 302 Found
Location: login.jsp
...
$ curl -I https://collaborate-corp.amazon.com/nuxeo/login.jsp;/..;/[unauthorized_area]
HTTP/1.1 500 Internal Server Error
...
从上可以看到,我们设计了绕过重定向进行身份验证的请求,但多数响应页面返回的是一个500的错误。因为 servlet 逻辑无法获取到一个有效的用户框架信息,所以它抛出了一个 Java 的NullPointerException异常。尽管出现这样的错误,我们还是一样可以从中找到突破口。(PS:除了这种方法之外,我还发现了另外一种快捷的入侵方式,留作下次再分享)
BUG 02:代码重用功能导致的部分表达式调用(EL invocation)
像我前述的,在Seam框架中其实存在很多风险隐患,因此,对于我来说,下一步就是尽量尝试利用上面分析的第一个BUG来实现对 Seam 中的 servlet 进行未授权访问测试。接下来,我会详细解释其中涉及的不同应用功能。
为了对浏览器的重定向跳转进行控制,Seam调用了一系列的HTTP参数,而且这些参数都存在隐患,如actionOutcome就是其中之一。在2013年,研究人员@meder就在其中发现了一个远程代码执行漏洞,你可以认真读读他的这篇文章-《 CVE-2010-1871: JBoss Seam Framework remote code execution》,但在这里,我们要来讨论的是另一个参数 - actionMethod。
actionMethod 是一个特殊的参数,它会从查询字符串中调用特定的 JBoss 表达式,这种方式貌似不安全,但调用也是有一些前提条件的。具体的实现过程可在callAction中查看到:
如果要调用表达式(EL),必须要满足以下前提条件:
1 参数actionMethod的值必须是配对的,也就是像这样的 FILENAME:EL_CODE
2 FILENAME部份的值必须是在Nuxeo中context-root下的真实文件
3 FILENAME对应的真实文件中必须包含"#{EL_CODE}"(包括两个双引号)
这种FILENAME对应的真实文件就像以下这个 login.xhtml 文件一样:
<div class="entry">
<div class="label">
<h:outputLabel id="UsernameLabel" for="username">Username:</h:outputLabel>
</div>
<div class="input">
<s:decorate id="usernameDecorate">
<h:inputText id="username" value="#{user.username}" required="true"></h:inputText>
</s:decorate>
</div>
</div>
这样,你就可以通过下述URL链接来调用表达式 user.username :
http://host/whatever.xhtml?actionMethod=/foo.xhtml:user.username
BUG 03:二次评估判断导致的表达式注入(EL injection)
上一个BUG中的功能错误看起来比较符合,但是却不能控制context-root下的任意文件,这样也就无法在远程目标服务器中调用任意表达式(EL)。然而这里却存在一个特别厉害的功能BUG....:
严重点说就是,如果上一个BUG能返回一个字符串,并且这个字符串看起来像一个表达式,那么 Seam 框架将会被再次调用!
以下是一些详细的调用栈信息:
callAction(Pages.java)
handleOutcome(Pages.java)
handleNavigation(SeamNavigationHandler.java)
interpolateAndRedirect(FacesManager.java)
interpolate(Interpolator.java)
interpolateExpressions(Interpolator.java)
createValueExpression(Expressions.java)
利用这个酷炫的功能,如果我们能控制返回值,也就能间接执行任意表达式(EL)了!这就有点像二进制漏洞利用中的返回导向编程技术(Return-Oriented Programming, ROP),所以,剩下来的就是要找到一个具备这种字符串返回功能的适合组件来实现BUG利用了。
在这个用例中,我们就选择widgets/suggest_add_new_directory_entry_iframe.xhtml中的组件:
<nxu:set var="directoryNameForPopup"
value="#{request.getParameter('directoryNameForPopup')}"
cache="true">
<nxu:set var="directoryNameForPopup"
value="#{nxu:test(empty directoryNameForPopup, select2DirectoryActions.directoryName, directoryNameForPopup)}"
cache="true">
<c:if test="#{not empty directoryNameForPopup}">
为什么选择这个呢?因为其中的request.getParameter会返回一个我们可以控制的,来自查询字符串中的字符串!虽然整个标记是为了分配一个变量,但我们可对其语法语义进行滥用!
所以,基于上述这段代码,我们可以把第二阶段 Payload 放到变量 directoryNameForPopup 中,然后再利用第一个BUG,综合起来就能实现无需验证的任意表达式(EL)执行了,以下是PoC:
http://host/nuxeo/login.jsp;/..;/create_file.xhtml
?actionMethod=widgets/suggest_add_new_directory_entry_iframe.xhtml:request.getParameter('directoryNameForPopup')
&directoryNameForPopup=/?#{HERE_IS_THE_EL}
难道就只能这样了吗?当然不!虽然我们可以执行任意表达式,但却无法成功反弹控制shell。接着往下看!
BUG 04:绕过表达式黑名单导致的远程代码执行漏洞(RCE)
Seam 官方也清楚其中的表达式语言(EL)存在的问题,所以,自Seam 2.2.2.Final版本之后,就在其中加入了一个新的表达式黑名单,用它来阻止一些不安全的调用!而且不幸的是, Nuxeo开源项目内置使用了 Seam 的 2.3.1.Final 最新版本,所以,我们必须要找到一种能成功绕过表达式黑名单的有效方法。而该表达式黑名单可以在 resources/org/jboss/seam/blacklist.properties 中找到:
.getClass(
.class.
.addRole(
.getPassword(
.removeRole(
经过一番钻研,我发现这种黑名单机制仅只是简单的字符串匹配规则,众所周知,黑名单机制通常都不算是一种好策略。初次看到这个黑名单,我就想到了对Struts2 S2-020的绕过方法,它和这里的黑名单绕过有着异曲同工之妙,它们都使用了类似数组的操作符来绕过黑名单机制,只需把这里的:
"".getClass().forName("java.lang.Runtime")
修改为:
""["class"].forName("java.lang.Runtime")
就OK了,是不是非常简单!是的!好了!
所以,现在最后的剩下的事情就是利用 JBoss 表达式语言(EL)编写 shellcode 了,采用Java 反射 API来获取 java.lang.Runtime 对象,并列出其中所有的涉及方法。getRuntime()方法会返回一个 Runtime 实例, exec(String)方法则会执行我们的预置命令!
综合以上Bug01、Bug02、Bug03和Bug04,就能实现RCE漏洞的执行。以下就是大致的实现步骤:
1、用路径规范化错误造成访问控制列表(ACL)绕过;
2、绕过白名单机制实现未授权的 Seam servlet 访问;
3、使用Seam功能的actionMethod参数去调用文件中的合适组件suggest_add_new_directory_entry_iframe.xhtml;
4、在HTTP参数directoryNameForPopup中准备第二阶段Payload;
5、使用类似数组的操作符来绕过表达式语言(EL)黑名单;
6、用 Java 反射型 API 来编写shellcode;
7、静待反弹控制 shell,成为黑客大佬。
以下就是整个漏洞利用exploit:
执行最终的Perl测试脚本,可以成功获取到反弹控制shell:
修复措施
JBoss
因为Seam框架存在的安全隐患最为直接,所以我曾在2016年9月曾把这些功能性Bug,以邮件方式通报给了它的官方应用商JBoss (security@jboss.org),但得到的却是这样的回复:
非常感谢你的漏洞通报。
目前,Seam只包含在我们 JBOSS 企业应用平台(EAP)的 5.0版本中,并不包含在其中的6和7版本中。而且,JBOSS 企业应用平台(EAP)即将在2016年11月停止维护更新,你用来测试的上游版本是3年前就发布的了。
在我们对JBOSS 企业应用平台(EAP)5.0版本的维护更新中,我们只接收一些高危或关键的漏洞问题。你强调的RCE漏洞实现,前提需要在攻击中先上传一个文件,所以某种程度上来说,这就会致使漏洞影响降级。
在Seam项目生命周期的这个阶段,我们不会费心去修复这类安全问题。非常感谢,也希望你今后继续向我们通报安全问题。
所以,由于项目的终止问题,这些问题功能Bug从未得到过官方的补丁修复,但是,现实网络世界中却还存在着大量基于Seam的应用。所以,如果你用了Seam,建议你尽快采用Nuxeo给出的方案来缓解这些问题。
Amazon
经过快速响应调查,Amazon安全团队第一时间隔离了存在漏洞的协同平台服务器,和我及时讨论了相关缓解措施,并告知了我他们具体的修复措施。
Nuxeo
经由Amazon的通报,Nuxeo也快速的释出了8.10的一个补丁,该补丁通过覆盖了callAction() 方法来修复了其中的功能性Bug。具体请参考这里的补丁文件。
漏洞上报进程
2018年3月10日01:13 通过邮箱aws-security@amazon.com向Amazon安全团队通报漏洞
2018年3月10日01:38 Amazon回应正在展开分析调查
2018年3月10日03:12 Amazon请求我是否能参加他们安全团队的电话会议
2018年3月10日05:30 与Amazon召开电话会议,对他们的漏洞修复措施进行了了解
2018年3月10日16:05 询问Amazon是否我可以在Black Hat大会上公开披露这个漏洞
2018年3月15日04:58 Nuxeo发布了8.10的新版本,修复了其中的RCE漏洞
2018年3月15日23:00 与Amazon召开电话会议,了解当前修复情况并讨论漏洞披露细节
2018年4月 5日05:40 获得Amazon赏金
*参考来源:Orange,clouds编译,转载请注明来自FreeBuf.COM