VMWare vCenter 通过 vCloud Director 实现接管 (CVE-2020–3956)
Chetan Conikee
5 分钟阅读 · 2020年6月4日
Citadelo 的安全研究人员披露了一个基于表达式语言(EL)的注入漏洞,该漏洞允许已认证的攻击者发送恶意载荷(通过 API 调用或拦截的 Web 请求),从而导致:
- 权限提升 — “组织管理员”(租户账户)提升至“系统管理员”(hypervisor)
- 跨租户横向移动
- 敏感基础设施信息泄露
- 获取密码和凭据以进一步提权
在本博客中,我将尝试解构这个已识别的零日漏洞(CVE-2020–3956)及其披露过程,并总结可以吸取的教训。
Citadelo 的 Tomas Melicher 和 Lukas Vaclavik 在渗透测试期间发现了 VMware Cloud Director 中的这一漏洞,并向厂商报告。VMware 获知后发布了安全公告,并推出了修复该漏洞的新版本产品。目前尚无针对旧版本的独立补丁。VMware 为暂时无法更新的客户提供了变通方案。
~ 摘自 Citadelo 披露信息
基于 Citadelo 研究人员的负责任的披露,VMware 已发布修复补丁并分发给其客户群。
理解 VMware vCloud Director 表达式语言注入的利用过程
免责声明:首先我要说明,我没有内部消息。以下是我根据 Citadelo 研究人员公开的信息做出的最佳推测。
- 注册 vCloud Director 试用账户
- 与 vCloud Director 的 SMTP 服务器进行 API 交互,并通过 Burp/ZAP 代理拦截响应
- 在一次请求序列中,将
${7*7}替换为 SMTP 服务器的主机名,并观察响应以确认反馈- 返回字符串:
value has invalid format, value: [49] - 这明确表明表达式被成功解析
- 返回字符串:
- 他们进一步验证了执行任意 Java 命令的能力
- 经过多次尝试,最终有效载荷成功实现了基于 EL 的远程命令注入:
${''.getClass().forName('java.io.BufferedReader').getDeclaredConstructors()[1].newInstance(''.getClass().forName('java.io.InputStreamReader').getDeclaredConstructors()[3].newInstance(''.getClass().forName('java.lang.ProcessBuilder').getDeclaredConstructors()[0].newInstance(['bash','-c','id']).start().getInputStream())).readLine()}
- 获得远程命令执行能力后,研究人员转向通过目录列举从
*.properties文件中提取敏感信息,进而研究应用于 vCloud 后台数据库中凭据的加密/解密方案:base64(sha512(password+salt)) - 在获得后台数据库访问权限后,他们对“系统管理员”账户发起密码更改操作,从而将权限从租户权限提升至系统权限
- 至此,他们建立了对 hypervisor 上托管的所有租户(客户)的完全控制
图片来源:Citadelo (citadelo.com/en/blog/ful…)
影响范围
- 使用 VMware vCloud Director 的公有云提供商
- 使用 VMware vCloud Director 的私有云提供商
- 使用 VMware vCloud Director 技术的企业
- 任何使用 VMware Cloud Director 的政府实体
基于 Shodan 的搜索显示,当前有超过 1,890 个被索引且暴露在外的 vCloud Director 接口。
什么是表达式语言注入
“表达式语言”是作为 JSTL(JavaServer Pages 标准标签库)的一部分开发的,旨在方便将对象模型(抽象类型)导出到 JSP 视图接口。在 JSP 页面中被采纳后,其用途也扩展到了非视图接口。
这是框架作者提供的一项特性,用于实现“生成代码的代码”这一范式。
代码示例说明
通过 GET/POST 请求发起的利用:
http://xxxx.com/provision?scope=${applicationScope}
发送到包含以下代码的 Web 前端页面:
<spring:message text=""code="${param['scope']}"></spring:message>
将导致输出结果能够泄露内部服务器信息,包括类路径和本地工作目录。
非恶意载荷示例:
${1+1}
${host.name}
恶意载荷示例:
${pageContext.getClass().getClassLoader().getParent().newInstance(pageContext.request.getSession().getAttribute("arr").toArray(pageContext.getClass().getClassLoader().getParent().getURLs())).loadClass("Malicious").newInstance()}
Java 统一表达式语言模拟示例
import de.odysseus.el.ExpressionFactoryImpl;
import de.odysseus.el.util.SimpleContext;
import javax.el.*;
public class Main {
public static void main(String[] args) {
ExpressionFactory expFactory = new ExpressionFactoryImpl();
SimpleContext context = new SimpleContext();
String el = "ABC ${true.toString().toUpperCase()}";
ValueExpression e = expFactory.createValueExpression(context, el, String.class);
System.out.println(e.getValue(context));
}
}
表达式语言规范的其他实现
EL 规范(JSR-242/JSR-245)的许多其他非视图实现:
- Java 统一表达式语言
- Apache Jakarta
- Apache Struts/WebWork (OGNL)
- MVEL 语言 — 用于控制台应用
- Spring SPEL
- JEXL/JUEL/JSR 341
- Apache Velocity
- Freemarker
- Seam
- Google Web Toolkit
- PrimeFaces
- Apache MyFaces
- RichFaces
- Node.js 相关实现
- 以及更多
表达式语言实现如何被利用?
攻击者目标:通过表达式语言解析不受信任的输入,实现信息泄露、权限提升或远程代码执行。
暴露源类型(通过 Web 服务):
- API
- 拦截 Web 界面表单
- 操纵 HTTP 头部
- 暴露的管理接口(数据库、缓存、DNS、Web 服务器)
敏感接收器类型(基于表达式语言):
.eval(UNTRUSTED_INPUT_FROM_SOURCE).getValue(UNTRUSTED_INPUT_FROM_SOURCE).instance_eval(UNTRUSTED_INPUT_FROM_SOURCE).invoke(UNTRUSTED_INPUT_FROM_SOURCE).from_string(UNTRUSTED_INPUT_FROM_SOURCE).render(UNTRUSTED_INPUT_FROM_SOURCE).sockets(UNTRUSTED_INPUT_FROM_SOURCE).file(UNTRUSTED_INPUT_FROM_SOURCE)render inline: UNTRUSTED_INPUT_FROM_SOURCE- 以及更多
理解基于 EL 的 CVE-2017-5638
(该漏洞同样基于表达式语言注入,导致远程代码执行。)
主动安全防御
您可能认为自己的边界防御足够强大,能够检测到大多数此类威胁。然而,攻击者早已了解 reactive 型安全工具的运作方式,并以绕过它们为己任。
除了扫描配置和 OSS 依赖项之外,关键是要通过污点流分析来检查应用程序的逻辑是如何使用其所部署的 OSS 依赖项和框架的。
利用这种图查询语言,我们可以提出以下问题来预先判断任何代码库是否容易受到此类基于表达式语言的漏洞攻击。 CSD0tFqvECLokhw9aBeRqpPFUGAgCJyqKlenpNuk7milkckB2PWNGfs50RV57dWK6uqIOXcsOnD5u4R/pbtQj+gRjjMexZxQPlSs0Q7MS1KDEbifTYq680XfrxaHHx6utiu7QAoxfLSppjTeKF2/r7kDQswwy5aKDx9susTiGOOQbVbVj7EhL1vO7epBBbkD