Module: Msf::Exploit::Remote::DNS::Client

Includes:
Common, Tcp, Udp
Included in:
Enumeration
Defined in:
lib/msf/core/exploit/dns/client.rb

Constant Summary

Constants included from Common

Msf::Exploit::Remote::DNS::Common::MATCH_HOSTNAME, Msf::Exploit::Remote::DNS::Common::Packet

Instance Attribute Summary

Attributes included from Tcp

#sock

Attributes included from Udp

#udp_sock

Instance Method Summary collapse

Methods included from Tcp

#chost, #cleanup, #connect, #connect_timeout, #cport, #deregister_tcp_options, #disconnect, #handler, #lhost, #lport, #peer, #print_prefix, #proxies, #rhost, #rport, #set_tcp_evasions, #shutdown, #ssl, #ssl_cipher, #ssl_verify_mode, #ssl_version

Methods included from Udp

#chost, #cleanup, #connect_udp, #cport, #deregister_udp_options, #disconnect_udp, #handler, #lhost, #lport, #rhost, #rport

Instance Method Details

#clientObject

Convenience method for DNS resolver as client Executes setup_resolver if none exists


191
192
193
194
# File 'lib/msf/core/exploit/dns/client.rb', line 191

def client
  setup_resolver unless @dns_resolver
  @dns_resolver
end

#initialize(info = {}) ⇒ Object

Initializes an exploit module that interacts with a DNS server.


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/msf/core/exploit/dns/client.rb', line 23

def initialize(info = {})
  super

  deregister_options('RHOST')
  register_options(
    [
      Opt::RPORT(53),
      Opt::Proxies,
      OptString.new('DOMAIN', [ false, "The target domain name"]),
      OptString.new('NS', [ false, "Specify the nameservers to use for queries, space separated" ]),
      OptString.new('SEARCHLIST', [ false, "DNS domain search list, comma separated"]),
      OptInt.new('THREADS', [true, "Number of threads to use in threaded queries", 1])
    ], Exploit::Remote::DNS::Client
  )

  register_advanced_options(
    [
      OptString.new('DnsClientDefaultNS', [ false, "Specify the default to use for queries, space separated", '8.8.8.8 8.8.4.4' ]),
      OptInt.new('DnsClientRetry', [ false, "Number of times to try to resolve a record if no response is received", 2]),
      OptInt.new('DnsClientRetryInterval', [ false, "Number of seconds to wait before doing a retry", 2]),
      OptBool.new('DnsClientReportARecords', [false, "Add hosts found via BRT and RVL to DB", true]),
      OptBool.new('DnsClientRVLExistingOnly', [false, "Only perform lookups on hosts in DB", true]),
      OptBool.new('DnsClientTcpDns', [false, "Run queries over TCP", false]),
      OptPath.new('DnsClientResolvconf', [true, "Resolvconf formatted configuration file to use for Resolver", "/dev/null"])
    ], Exploit::Remote::DNS::Client
  )

  register_autofilter_ports([ 53 ]) if respond_to?(:register_autofilter_ports)
  register_autofilter_services(%W{ dns }) if respond_to?(:register_autofilter_services)
end

#process_nameserversObject

Sets the resolver's nameservers Uses explicitly defined NS option if set Uses RHOSTS if not explicitly defined


200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/msf/core/exploit/dns/client.rb', line 200

def process_nameservers
  if datastore['NS'].blank?
    nameservers = datastore['DnsClientDefaultNS'].split(/\s|,/)
  else
    nameservers = datastore['NS'].split(/\s|,/)
  end

  invalid = nameservers.select { |ns| !Rex::Socket.dotted_ip?(ns) }
  if !invalid.empty?
    raise "Nameservers must be IP addresses. The following were invalid: #{invalid.join(", ")}"
  end

  nameservers
end

#query(domain = datastore['DOMAIN'], type = 'A') ⇒ Dnsruby::RR

Convenience wrapper around Resolver's query method - send DNS request

Parameters:

  • domain (String) (defaults to: datastore['DOMAIN'])

    Domain for which to request a record

  • type (String) (defaults to: 'A')

    Type of record to request for domain

Returns:

  • (Dnsruby::RR)

    DNS response


62
63
64
# File 'lib/msf/core/exploit/dns/client.rb', line 62

def query(domain = datastore['DOMAIN'], type = 'A')
  client.query(domain, type)
end

#query_async(queries = [], threadmax = datastore['THREADS'], &block) ⇒ Array

Performs a set of asynchronous lookups for an array of domain,type pairs

Parameters:

  • queries (Array) (defaults to: [])

    Set of domain,type pairs to pass into #query

  • threadmax (Fixnum) (defaults to: datastore['THREADS'])

    Max number of running threads at a time

  • block (Proc)

    Code block to execute with the query result

Returns:

  • (Array)

    Resulting set of responses or responses processed by passed blocks


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/msf/core/exploit/dns/client.rb', line 74

def query_async(queries = [], threadmax = datastore['THREADS'], &block)
  running = []
  while !queries.empty?
    domain, type = queries.shift
    running << framework.threads.spawn("Module(#{self.refname})-#{domain} #{type}", false) do |qat|
      if block
        block.call(query(domain,type))
      else
        query(domain,type)
      end
    end
    while running.select(&:alive?).count >= threadmax
      Rex::ThreadSafe.sleep(1)
    end
  end
  return running.join
end

#set_nameserver(ns = []) ⇒ Object

Switch DNS forwarders in resolver with thread safety

Parameters:

  • ns (Array, String) (defaults to: [])

    List of (or single) nameservers to use


96
97
98
99
100
101
102
103
# File 'lib/msf/core/exploit/dns/client.rb', line 96

def set_nameserver(ns = [])
  if ns.respond_to?(:split)
    ns = [ns]
  end
  @lock.synchronize do
    @dns_resolver.nameserver = ns.flatten
  end
end

#setup_resolverObject

Create and configure Resolver object


152
153
154
155
156
157
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/msf/core/exploit/dns/client.rb', line 152

def setup_resolver
  options.validate(datastore) # This is a hack, DS values should not be Strings prior to this
  config = {
    :config_file => datastore['DnsClientResolvconf'],
    :nameservers => process_nameservers,
    :port => datastore['RPORT'],
    :retry_number => datastore['DnsClientRetry'].to_i,
    :retry_interval => datastore['DnsClientRetryInterval'].to_i,
    :use_tcp => datastore['DnsClientTcpDns'],
    :context => {'Msf' => framework, 'MsfExploit' => self}
  }
  if datastore['SEARCHLIST']
    if datastore['SEARCHLIST'].split(',').all? do |search|
      search.match(MATCH_HOSTNAME)
    end
      config[:search_list] = datastore['SEARCHLIST'].split(',')
    else
      raise 'Domain search list must consist of valid domains'
    end
  end
  if datastore['CHOST']
    config[:source_address] = IPAddr.new(datastore['CHOST'].to_s)
  end
  if datastore['CPORT']
    config[:source_port] = datastore['CPORT'] unless datastore['CPORT'] == 0
  end
  if datastore['Proxies']
    vprint_status("Using DNS/TCP resolution for proxy config")
    config[:use_tcp] = true
    config[:proxies] = datastore['Proxies']
  end
  @dns_resolver_lock = Mutex.new unless @dns_resolver_lock
  @dns_resolver = Rex::Proto::DNS::Resolver.new(config)
end

#switchdns(domain) ⇒ Object

Switch nameservers to use explicit NS or SOA for target

Parameters:

  • domain (String)

    Domain for which to find SOA


109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/msf/core/exploit/dns/client.rb', line 109

def switchdns(domain)
  if datastore['NS'].blank?
    resp_soa = client.query(target, "SOA")
    if (resp_soa)
      (resp_soa.answer.select { |i| i.is_a?(Dnsruby::RR::SOA)}).each do |rr|
        resp_1_soa = client.search(rr.mname)
        if (resp_1_soa and resp_1_soa.answer[0])
          set_nameserver(resp_1_soa.answer.map(&:address).compact.map(&:to_s))
          print_status("Set DNS Server to #{target} NS: #{client.nameserver.join(', ')}")
          break
        end
      end
    end
  else
    vprint_status("Using DNS Server: #{client.nameserver.join(', ')}")
    client.nameserver = process_nameservers
  end
end

#wildcard(domain, type = "A") ⇒ String

Detect if target has wildcards enabled for a record type

Parameters:

  • target (String)

    Domain to test

  • type (String) (defaults to: "A")

    Record type to test

Returns:

  • (String)

    Address which is returned for wildcard requests


135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/msf/core/exploit/dns/client.rb', line 135

def wildcard(domain, type = "A")
  addr = false
  rendsub = rand(10000).to_s
  response = query("#{rendsub}.#{target}", type)
  if response.answer.length != 0
    vprint_status("This domain has wildcards enabled!!")
    response.answer.each do |rr|
      print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Dnsruby::RR::CNAME
      addr = rr.address.to_s
    end
  end
  return addr
end