diff --git a/lib/msf/core/post/dns.rb b/lib/msf/core/post/dns.rb new file mode 100644 index 000000000000..3034a9f84761 --- /dev/null +++ b/lib/msf/core/post/dns.rb @@ -0,0 +1,4 @@ +# -*- coding: binary -*- + +module Msf::Post::DNS +end diff --git a/lib/msf/core/post/dns/resolve_host.rb b/lib/msf/core/post/dns/resolve_host.rb new file mode 100644 index 000000000000..79909018a39c --- /dev/null +++ b/lib/msf/core/post/dns/resolve_host.rb @@ -0,0 +1,48 @@ +# -*- coding: binary -*- + +module Msf + class Post + module DNS + ### + # + # This module resolves session DNS + # + ### + module ResolveHost + # Takes the host name and makes use of nsloopup to resolve the IP + # + # @param [String] host Hostname + # @return [String, nil] ip The resolved IP + def resolve_host(host) + ip = nil + + if client.respond_to?(:net) && client.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST) + result = client.net.resolve.resolve_host(host) + + return if result[:ip].blank? + + ip = result[:ip] + else + data = cmd_exec("nslookup #{host}") + if data =~ /Name/ + # Remove unnecessary data and get the section with the addresses + returned_data = data.split(/Name:/)[1] + # check each element of the array to see if they are IP + returned_data.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ').each do |e| + if Rex::Socket.dotted_ip?(e) + ip = e + end + end + end + end + + if ip.nil? + print_error("Could not resolve IP for #{host}") + else + ip + end + end + end + end + end +end diff --git a/modules/post/windows/gather/enum_computers.rb b/modules/post/windows/gather/enum_computers.rb index 5e176bec1d9f..0965fea8311e 100644 --- a/modules/post/windows/gather/enum_computers.rb +++ b/modules/post/windows/gather/enum_computers.rb @@ -5,8 +5,9 @@ class MetasploitModule < Msf::Post include Msf::Post::File - + include Msf::Post::Windows::Accounts include Msf::Post::Windows::Registry + include Msf::Post::DNS::ResolveHost def initialize(info = {}) super( @@ -14,12 +15,17 @@ def initialize(info = {}) info, 'Name' => 'Windows Gather Enumerate Computers', 'Description' => %q{ - This module will enumerate computers included in the primary Domain. + This module will enumerate computers included in the primary Active Directory domain. }, 'License' => MSF_LICENSE, 'Author' => [ 'Joshua Abraham '], 'Platform' => [ 'win'], - 'SessionTypes' => [ 'meterpreter' ], + 'SessionTypes' => %w[meterpreter powershell shell], + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [], + 'SideEffects' => [] + }, 'Compat' => { 'Meterpreter' => { 'Commands' => %w[ @@ -31,74 +37,65 @@ def initialize(info = {}) ) end - # Run Method for when run command is issued def run - print_status("Running module against #{sysinfo['Computer']}") if !sysinfo.nil? - domain = get_domain - if !domain.empty? - hostname_list = get_domain_computers - list_computers(domain, hostname_list) + hostname = sysinfo.nil? ? cmd_exec('hostname') : sysinfo['Computer'] + print_status("Running module against #{hostname} (#{session.session_host})") + + domain = get_domain_name + + fail_with(Failure::Unknown, 'Could not retrieve domain name. Is the host part of a domain?') unless domain + + netbios_domain_name = domain.split('.').first.upcase + + hostname_list = get_domain_computers + + if hostname_list.empty? + print_error('No computers found') + return end + + list_computers(netbios_domain_name, hostname_list) end def gethost(hostname) ## get IP for host vprint_status("Looking up IP for #{hostname}") - result = client.net.resolve.resolve_host(hostname) - - return nil if result[:ip].nil? || result[:ip].blank? - - return result[:ip] + resolve_host(hostname) end - # List Members of a domain group def get_domain_computers computer_list = [] devisor = "-------------------------------------------------------------------------------\r\n" raw_list = cmd_exec('net view').split(devisor)[1] - if raw_list =~ /The command completed successfully/ - raw_list.sub!(/The command completed successfully\./, '') - raw_list.gsub!(/\\\\/, '') - raw_list.split(' ').each do |m| - computer_list << m - end - end - return computer_list - end + return [] unless raw_list.include?('The command completed successfully') - # Gets the Domain Name - def get_domain - domain = '' - begin - subkey = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\History' - v_name = 'DCName' - domain_dc = registry_getvaldata(subkey, v_name) - dom_info = domain_dc.split('.') - domain = dom_info[1].upcase - rescue StandardError - print_error('This host is not part of a domain.') + raw_list.sub!(/The command completed successfully\./, '') + raw_list.gsub!(/\\\\/, '') + raw_list.split(' ').each do |m| + computer_list << m end - return domain + + computer_list end def list_computers(domain, hosts) tbl = Rex::Text::Table.new( - 'Header' => 'List of Domain Hosts for the primary Domain.', + 'Header' => 'List of identified Hosts.', 'Indent' => 1, 'Columns' => - [ - 'Domain', - 'Hostname', - 'IPs', - ] + [ + 'Domain', + 'Hostname', + 'IPs', + ] ) hosts.each do |hostname| hostip = gethost(hostname) tbl << [domain, hostname, hostip] end - results = tbl.to_s - print_line("\n" + results + "\n") + + print_line("\n#{tbl}\n") report_note( host: session,