Skip to content

Commit

Permalink
Land #18971, Adjust multiple DNS related things
Browse files Browse the repository at this point in the history
Merge branch 'land-18971' into upstream-master
  • Loading branch information
bwatters-r7 committed Mar 19, 2024
2 parents 7f761d0 + b3b6f79 commit 1e47b33
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 11 deletions.
34 changes: 31 additions & 3 deletions docs/metasploit-framework.wiki/How-to-Configure-DNS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ handling the request. Different resolver types can be specified to handle querie
in numeric order starting at position 1. Rules can be added to or removed from specific positions in a similar manner to
how iptables rules can be added to and removed from a specific chain.

### The Blackhole Resolver
The blackhole resolver can be used to prevent queries from being resolved. It handles all query types and will prevent
resolvers defined after it from being used. The blackhole resolver is specified by using the `blackhole` keyword.
### The Black Hole Resolver
The black hole resolver can be used to prevent queries from being resolved. It handles all query types and will prevent
resolvers defined after it from being used. The black hole resolver is specified by using the `black-hole` keyword.

### The Upstream Resolver
An upstream resolver can be used by specifying either an IPv4 or IPv6 address. When Metasploit uses this resolver, the
Expand Down Expand Up @@ -106,6 +106,12 @@ Append a rule to the end that will handle all queries for `*.lab.lan` using an u
dns add --rule *.lab.lan --session 1 192.0.2.1
```

Append a rule to drop all queries for `*.noresolve.lan` using the black hole resolver.

```
dns add --rule *.noresolve.lan black-hole
```

## Static DNS Entries
Static entries used by the static resolver are configured through the `add-static` and `remove-static` subcommands. The
currently configured entries can be viewed in the `dns print` output and all entries can be flushed with the
Expand All @@ -114,6 +120,28 @@ is specified. In order for the static entry to be used, at least one rule must m
configured to use the static resolver. A single hostname can be associated with multiple IP addresses and the same IP
address can be associated with multiple hostnames.

### Example Static Entries

Define static entries for `localhost` and common variations.

```
dns add-static localhost 127.0.0.1 ::1
dns add-static localhost4 127.0.0.1
dns add-static localhost6 ::1
```

Remove all static entries for `localhost`.

```
dns remove-static localhost
```

Remove all static entries.

```
dns flush-static
```

## The DNS Cache
DNS query replies are cached internally by Metasploit based on their TTL. This intends to minimize the amount of network
traffic required to perform the necessary lookups. The number of query replies that are currently cached is available in
Expand Down
24 changes: 17 additions & 7 deletions lib/msf/ui/console/command_dispatcher/dns.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,12 @@ def add_dns(*args)

resolvers.each do |resolver|
unless Rex::Proto::DNS::UpstreamRule.valid_resolver?(resolver)
raise ::ArgumentError.new("Invalid DNS resolver: #{resolver}")
message = "Invalid DNS resolver: #{resolver}."
if (suggestions = Rex::Proto::DNS::UpstreamRule.spell_check_resolver(resolver)).present?
message << " Did you mean #{suggestions.first}?"
end

raise ::ArgumentError.new(message)
end
end

Expand All @@ -302,7 +307,7 @@ def add_dns(*args)
end

rules.each_with_index do |rule, offset|
print_warning("DNS rule #{rule} does not contain wildcards, so will not match subdomains") unless rule.include?('*')
print_warning("DNS rule #{rule} does not contain wildcards, it will not match subdomains") unless rule.include?('*')
driver.framework.dns_resolver.add_upstream_rule(
resolvers,
comm: comm_obj,
Expand Down Expand Up @@ -639,11 +644,16 @@ def print_dns
'SortIndex' => -1,
'WordWrap' => false
)
resolver.static_hostnames.each do |hostname, addresses|
ipv4_addresses = addresses.fetch(Dnsruby::Types::A, [])
ipv6_addresses = addresses.fetch(Dnsruby::Types::AAAA, [])
0.upto([ipv4_addresses.length, ipv6_addresses.length].max - 1) do |idx|
tbl << [idx == 0 ? hostname : TABLE_INDENT, ipv4_addresses[idx], ipv6_addresses[idx]]
resolver.static_hostnames.sort_by { |hostname, _| hostname }.each do |hostname, addresses|
ipv4_addresses = addresses.fetch(Dnsruby::Types::A, []).sort_by(&:to_i)
ipv6_addresses = addresses.fetch(Dnsruby::Types::AAAA, []).sort_by(&:to_i)
if (ipv4_addresses.length <= 1 && ipv6_addresses.length <= 1) && ((ipv4_addresses + ipv6_addresses).length > 0)
tbl << [hostname, ipv4_addresses.first, ipv6_addresses.first]
else
tbl << [hostname, '', '']
0.upto([ipv4_addresses.length, ipv6_addresses.length].max - 1) do |idx|
tbl << [TABLE_INDENT, ipv4_addresses[idx], ipv6_addresses[idx]]
end
end
end
print_line(tbl.to_s)
Expand Down
2 changes: 1 addition & 1 deletion lib/rex/proto/dns/static_hostnames.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module DNS
class StaticHostnames
extend Forwardable

def_delegators :@hostnames, :each, :each_with_index, :length, :empty?
def_delegators :@hostnames, :each, :each_with_index, :length, :empty?, :sort_by

# @param [Hash<String, IPAddr>] hostnames The hostnames to IP address mappings to initialize with.
def initialize(hostnames: nil)
Expand Down
17 changes: 17 additions & 0 deletions lib/rex/proto/dns/upstream_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ def self.valid_resolver?(resolver)
].include?(resolver)
end

# Perform a spell check on resolver to suggest corrections.
#
# @param [String] resolver The resolver string to check.
# @rtype [Nil, Array<String>] The suggestions if resolver is invalid.
def self.spell_check_resolver(resolver)
return nil if Rex::Socket.is_ip_addr?(resolver)

suggestions = DidYouMean::SpellChecker.new(dictionary: [
UpstreamResolver::Type::BLACK_HOLE,
UpstreamResolver::Type::STATIC,
UpstreamResolver::Type::SYSTEM
]).correct(resolver).map(&:to_s)
return nil if suggestions.empty?

suggestions
end

# Check whether or not the defined wildcard is a valid pattern.
#
# @param [String] wildcard The wildcard text to check.
Expand Down
22 changes: 22 additions & 0 deletions spec/lib/rex/proto/dns/upstream_rule_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
require 'rex/text'

RSpec.describe Rex::Proto::DNS::UpstreamRule do
describe '.spell_check_resolver' do
it 'returns nil for IPv4 addresses' do
address = Rex::Socket.addr_ntoa(Random.new.bytes(4))
expect(described_class.spell_check_resolver(address)).to be_nil
end

it 'returns nil for IPv6 addresses' do
address = Rex::Socket.addr_ntoa(Random.new.bytes(16))
expect(described_class.spell_check_resolver(address)).to be_nil
end

it 'returns nil for "black-hole"' do
expect(described_class.spell_check_resolver('black-hole')).to be_nil
end

it 'returns a populated array for "blackhole"' do
suggestions = described_class.spell_check_resolver('blackhole')
expect(suggestions).to be_a Array
expect(suggestions.first).to eq 'black-hole'
end
end

describe '.valid_resolver?' do
it 'returns true for "black-hole"' do
expect(described_class.valid_resolver?('black-hole')).to be_truthy
Expand Down

0 comments on commit 1e47b33

Please sign in to comment.