Module: Rex::Proto::DNS::CustomNameserverProvider

Defined in:
lib/rex/proto/dns/custom_nameserver_provider.rb

Overview

Provides a DNS resolver the ability to use different nameservers for different requests, based on the domain being queried.

Defined Under Namespace

Classes: CommSink

Constant Summary collapse

CONFIG_KEY_BASE =
'framework/dns'
CONFIG_VERSION =
Rex::Version.new('1.0')

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(mod) ⇒ Object



187
188
189
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 187

def self.extended(mod)
  mod.init
end

Instance Method Details

#add_upstream_rule(resolvers, comm: nil, wildcard: '*', index: -1)) ⇒ Object

Add a custom nameserver entry to the custom provider.

Parameters:

  • resolvers (Array<String>)

    The list of upstream resolvers that would be used for this custom rule.

  • comm (Msf::Session::Comm) (defaults to: nil)

    The communication channel to be used for these DNS requests.

  • wildcard (String) (defaults to: '*')

    The wildcard rule to match a DNS request against.

  • index (Integer) (defaults to: -1))

    The index at which to insert the rule, defaults to -1 to append it at the end.



124
125
126
127
128
129
130
131
132
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 124

def add_upstream_rule(resolvers, comm: nil, wildcard: '*', index: -1)
  resolvers = [resolvers] if resolvers.is_a?(String) # coerce into an array of strings

  @upstream_rules.insert(index, UpstreamRule.new(
    wildcard: wildcard,
    resolvers: resolvers,
    comm: comm
  ))
end

#flushObject



149
150
151
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 149

def flush
  @upstream_rules.clear
end

#has_config?Boolean

Check whether or not there is configuration data in Metasploit's configuration file which is persisted on disk.

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 70

def has_config?
  config = Msf::Config.load
  version = config.fetch(CONFIG_KEY_BASE, {}).fetch('configuration_version', nil)
  if version.nil?
    @logger.info 'DNS configuration can not be loaded because the version is missing'
    return false
  end

  their_version = Rex::Version.new(version)
  if their_version > CONFIG_VERSION # if the config is newer, it's incompatible (we only guarantee backwards compat)
    @logger.info "DNS configuration version #{their_version} can not be loaded because it is too new"
    return false
  end

  my_minimum_version = Rex::Version.new(CONFIG_VERSION.canonical_segments.first.to_s)
  if their_version < my_minimum_version # can not be older than our major version
    @logger.info "DNS configuration version #{their_version} can not be loaded because it is too old"
    return false
  end

  true
end

#initObject



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 36

def init
  @upstream_rules = []

  resolvers = [UpstreamResolver.create_static]
  if @config[:nameservers].empty?
    # if no nameservers are specified, fallback to the system
    resolvers << UpstreamResolver.create_system
  else
    # migrate the originally configured name servers
    resolvers += @config[:nameservers].map(&:to_s)
    @config[:nameservers].clear
  end

  add_upstream_rule(resolvers)

  nil
end

#load_configObject

Load the custom settings from the MSF config file



109
110
111
112
113
114
115
116
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 109

def load_config
  unless has_config?
    raise ResolverError.new('There is no compatible configuration data to load')
  end

  load_config_entries
  load_config_static_hostnames
end

#reinitObject

Reinitialize the configuration to its original state.



55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 55

def reinit
  parse_config_file
  parse_environment_variables

  self.static_hostnames.flush
  self.static_hostnames.parse_hosts_file

  init

  cache.flush if respond_to?(:cache)

  nil
end

#remove_ids(ids) ⇒ Array<UpstreamRule>

Remove upstream rules with the given indexes Ignore entries that are not found

Parameters:

  • ids (Array<Integer>)

    The IDs to removed

Returns:



139
140
141
142
143
144
145
146
147
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 139

def remove_ids(ids)
  removed = []
  ids.sort.reverse.each do |id|
    upstream_rule = @upstream_rules.delete_at(id)
    removed << upstream_rule if upstream_rule
  end

  removed.reverse
end

#save_configObject

Save the custom settings to the MSF config file



96
97
98
99
100
101
102
103
104
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 96

def save_config
  new_config = {
    'configuration_version' => CONFIG_VERSION.to_s
  }
  Msf::Config.save(CONFIG_KEY_BASE => new_config)

  save_config_upstream_rules
  save_config_static_hostnames
end

#set_framework(framework) ⇒ Object



191
192
193
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 191

def set_framework(framework)
  self.feature_set = framework.features
end

#upstream_resolvers_for_packet(packet) ⇒ Array<Array>

The nameservers that match the given packet

Parameters:

  • packet (Dnsruby::Message)

    The DNS packet to be sent

Returns:

  • (Array<Array>)

    A list of nameservers, each with Rex::Socket options

Raises:

  • (ResolveError)

    If the packet contains multiple questions, which would end up sending to a different set of nameservers



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 158

def upstream_resolvers_for_packet(packet)
  unless feature_set.enabled?(Msf::FeatureManager::DNS)
    return super
  end
  # Leaky abstraction: a packet could have multiple question entries,
  # and each of these could have different nameservers, or travel via
  # different comm channels. We can't allow DNS leaks, so for now, we
  # will throw an error here.
  results_from_all_questions = []
  packet.question.each do |question|
    name = question.qname.to_s
    upstream_rule = self.upstream_rules.find { |ur| ur.matches_name?(name) }

    if upstream_rule
      upstream_resolvers = upstream_rule.resolvers
    else
      # Fall back to default nameservers
      upstream_resolvers = super
    end
    results_from_all_questions << upstream_resolvers.uniq
  end
  results_from_all_questions.uniq!
  if results_from_all_questions.size != 1
    raise ResolverError.new('Inconsistent nameserver entries attempted to be sent in the one packet')
  end

  results_from_all_questions[0]
end

#upstream_rulesObject



195
196
197
# File 'lib/rex/proto/dns/custom_nameserver_provider.rb', line 195

def upstream_rules
  @upstream_rules.dup
end