Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve host mixin #19854

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/msf/core/post/dns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- coding: binary -*-

module Msf::Post::DNS
end
73 changes: 73 additions & 0 deletions lib/msf/core/post/dns/resolve_host.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: binary -*-

require 'rex/post/meterpreter/extensions/stdapi/constants'

module Msf
class Post
module DNS
###
#
# This module resolves session DNS
#
###
module ResolveHost
# Takes the host name and resolves the IP
#
# @param [String] host
# @param [Integer] family AF_INET for IPV4 and AF_INET6 for IPV6
# @return [Hash] The resolved IPs
def resolve_host(host, family)
if client.respond_to?(:net) && client.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST)
resolved_host = client.net.resolve.resolve_host(host, family)
# We want to only return the array off IPs
cgranleese-r7 marked this conversation as resolved.
Show resolved Hide resolved
resolved_host.reject { |k, _v| k == :ip }
else
ips = []
data = cmd_exec("nslookup #{host}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nslookup is Windows-specific, yet the resolve_host method doesn't seem to be platform-specific

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.each do |entry|
ip_list = entry.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ') - [host]
filtered_ips = filter_ips(ip_list, family)
ips = filtered_ips unless filtered_ips.empty?
end
# If nslookup responds with "no answer", fall back to resolving via host command
elsif data =~ /No answer/
data = cmd_exec("host #{host}")
if data =~ /has address/
# Remove unnecessary data and get the section with the addresses
returned_data = data.split("\n")[...-1]
# check each element of the array to see if they are IP
ip_list = returned_data.map { |entry| entry.split(' ').last }
filtered_ips = filter_ips(ip_list, family)
ips = filtered_ips unless filtered_ips.empty?
end
end
{ hostname: host, ips: ips }
end
end

# Takes the host and family and returns the IP address if it matches the appropriate family
# Needed to handle request that fallback to nslookup or host, as they return both IPV4 and IPV6.
#
# @param [Array] ips
# @param [Integer] family
# @return [Array] ips
def filter_ips(ips, family)
filtered_ips = []
ips.each do |ip|
if family == AF_INET
filtered_ips << ip if !!(ip =~ Resolv::IPv4::Regex)
elsif family == AF_INET6
filtered_ips << ip if !!(ip =~ Resolv::IPv6::Regex)
end
end

filtered_ips
end
end
end
end
end
55 changes: 29 additions & 26 deletions modules/post/windows/gather/enum_computers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'rex/post/meterpreter/extensions/stdapi/constants'

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(
Expand Down Expand Up @@ -58,36 +61,19 @@ def run

# Takes the host name and makes use of nslookup to resolve the IP
#
# @param [String] host Hostname
# @param [Object] hostname
# @param [Object] family
# @return [String] ip The resolved IP
def resolve_host(host)
vprint_status("Looking up IP for #{host}")
return host if Rex::Socket.dotted_ip?(host)

ip = []
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

if ip.blank?
'Not resolvable'
else
ip.join(', ')
end
def gethost(hostname, family)
## get IP for host
vprint_status("Looking up IP for #{hostname}")
resolve_host(hostname, family)
end

def get_domain_computers
computer_list = []
divisor = "-------------------------------------------------------------------------------\r\n"
net_view_response = cmd_exec('net view')
net_view_response = cmd_exec("cmd.exe", "/c net view")
unless net_view_response.include?(divisor)
print_error("The net view command failed with: #{net_view_response}")
return []
Expand All @@ -104,6 +90,7 @@ def get_domain_computers
end

def list_computers(domain, hosts)
meterpreter_dns_resolving_errors = []
tbl = Rex::Text::Table.new(
'Header' => 'List of identified Hosts.',
'Indent' => 1,
Expand All @@ -115,12 +102,28 @@ def list_computers(domain, hosts)
]
)
hosts.each do |hostname|
hostip = resolve_host(hostname)
tbl << [domain, hostname, hostip]
hostipv4 = gethost(hostname, AF_INET)
hostipv6 = gethost(hostname, AF_INET6)

if hostipv4[:ips].empty?
meterpreter_dns_resolving_errors << "IPV4: #{hostname} could not be resolved"
else
tbl << [domain, hostname, hostipv4[:ips].join(',')]
end

if hostipv6[:ips].empty?
meterpreter_dns_resolving_errors << "IPV6: #{hostname} could not be resolved" if hostipv6[:ips].empty?
else
tbl << [domain, hostname, hostipv6[:ips].join(',')] unless hostipv6[:ips].nil?
end
end

print_line("\n#{tbl}\n")

meterpreter_dns_resolving_errors.each do | error |
print_warning(error)
end

report_note(
host: session,
type: 'domain.hosts',
Expand Down
32 changes: 32 additions & 0 deletions spec/support/acceptance/command_shell/cmd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@ module Acceptance::Session
}
],
module_tests: [
{
name: 'post/test/resolve_host',
platforms: [
[
:linux,
{
skip: true,
reason: 'Payload not compiled for platform'
}
],
[
:osx,
{
skip: true,
reason: 'Payload not compiled for platform'
}
],
:windows
],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: []
}
}
},
{
name: 'post/test/cmd_exec',
platforms: [
Expand Down
26 changes: 26 additions & 0 deletions spec/support/acceptance/command_shell/linux.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ module Acceptance::Session
}
}
},
{
name: "post/test/resolve_host",
platforms: [
:linux,
:osx,
[
:windows,
{
skip: true,
reason: "Payload not compiled for platform"
}
]
],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: []
}
}
},
{
name: "post/test/cmd_exec",
platforms: [
Expand Down
32 changes: 32 additions & 0 deletions spec/support/acceptance/command_shell/powershell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@ module Acceptance::Session
}
],
module_tests: [
{
name: 'post/test/resolve_host',
platforms: [
[
:linux,
{
skip: true,
reason: 'Payload not compiled for platform'
}
],
[
:osx,
{
skip: true,
reason: 'Payload not compiled for platform'
}
],
:windows
],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: []
}
}
},
{
name: 'post/test/cmd_exec',
platforms: [
Expand Down
16 changes: 16 additions & 0 deletions spec/support/acceptance/session/java.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ module Acceptance::Session
}
}
},
{
name: "post/test/resolve_host",
platforms: [:linux, :osx, :windows],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: []
}
}
},
{
name: "post/test/cmd_exec",
platforms: [:linux, :osx, :windows],
Expand Down
26 changes: 26 additions & 0 deletions spec/support/acceptance/session/mettle.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ module Acceptance::Session
}
}
},
{
name: "post/test/resolve_host",
platforms: [
:linux,
:osx,
[
:windows,
{
skip: true,
reason: "Payload not compiled for platform"
}
]
],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: []
}
}
},
{
name: "post/test/cmd_exec",
platforms: [
Expand Down
17 changes: 17 additions & 0 deletions spec/support/acceptance/session/php.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ module Acceptance::Session
}
}
},
{
name: "post/test/resolve_host",
platforms: [:linux, :osx, :windows],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: [
]
}
}
},
{
name: "post/test/cmd_exec",
platforms: [:linux, :osx, :windows],
Expand Down
16 changes: 16 additions & 0 deletions spec/support/acceptance/session/python.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ module Acceptance::Session
}
}
},
{
name: "post/test/resolve_host",
platforms: [:linux, :osx, :windows],
skipped: false,
lines: {
linux: {
known_failures: []
},
osx: {
known_failures: []
},
windows: {
known_failures: []
}
}
},
{
name: "post/test/cmd_exec",
platforms: [:linux, :osx, :windows],
Expand Down
Loading
Loading