Module: Msf::Exploit::Remote::DNS::Enumeration

Includes:
Auxiliary::Report, Client
Defined in:
lib/msf/core/exploit/dns/enumeration.rb

Constant Summary

Constants included from Common

Common::MATCH_HOSTNAME, Common::Packet

Instance Attribute Summary

Attributes included from Tcp

#sock

Attributes included from Udp

#udp_sock

Instance Method Summary collapse

Methods included from Auxiliary::Report

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Methods included from Metasploit::Framework::Require

optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines

Methods included from Client

#client, #process_nameservers, #query, #query_async, #set_nameserver, #switchdns, #wildcard

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

#dns_axfr(domain) ⇒ Object


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 36

def dns_axfr(domain)
  nameservers = dns_get_ns(domain)
  return if nameservers.blank?
  records = []
  nameservers.each do |nameserver|
    next if nameserver.blank?
    print_status("Attempting DNS AXFR for #{domain} from #{nameserver}")
    dns = setup_resolver

    ns_a_records = []
    # try to get A record for nameserver from target NS, which may fail
    target_ns_a = dns_get_a(nameserver, 'DNS AXFR records')
    ns_a_records |= target_ns_a if target_ns_a
    ns_a_records << ::Rex::Socket.resolv_to_dotted(nameserver)
    begin
      dns.nameservers -= dns.nameservers
      dns.nameservers = ns_a_records
      zone = dns.axfr(domain)
    
    # Original set of exceptions which were deliberately caught but were missing some
    # situations. Leaving these in as they may want to be treated differently
    # to other, maybe more generic, exceptions.
    rescue ResolverArgumentError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, ::NoResponseError, ::Timeout::Error => e
      print_error("Query #{domain} DNS AXFR - exception: #{e}")
    rescue => e
      print_error("Query #{domain} DNS AXFR - unknown exception: #{e}")
    end
    next if zone.blank?
    records << zone
    print_good("#{domain} Zone Transfer: #{zone}")
  end
  return if records.blank?
  records
end

#dns_bruteforce(domain, wordlist, threads) ⇒ Object


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 71

def dns_bruteforce(domain, wordlist, threads)
  return if wordlist.blank?
  threads = 1 if threads <= 0

  queue = []
  File.foreach(wordlist) do |line|
    queue << "#{line.chomp}.#{domain}"
  end

  records = []
  until queue.empty?
    t = []
    threads = 1 if threads <= 0

    if queue.length < threads
      # work around issue where threads not created as the queue isn't large enough
      threads = queue.length
    end

    begin
      1.upto(threads) do
        t << framework.threads.spawn("Module(#{refname})", false, queue.shift) do |test_current|
          Thread.current.kill unless test_current
          a = dns_get_a(test_current, 'DNS bruteforce records', true)
          records |= a if a
        end
      end
      t.map(&:join)

    rescue ::Timeout::Error
    ensure
      t.each { |x| x.kill rescue nil }
    end
  end
  records
end

#dns_get_a(domain, type = 'DNS A records', displayed = false) ⇒ Object


108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 108

def dns_get_a(domain, type='DNS A records', displayed=false)
  resp = dns_query(domain, 'A')
  return if resp.blank? || resp.answer.blank?

  records = []
  resp.answer.each do |r|
    next unless r.class == Dnsruby::RR::IN::A
    records << r.address.to_s
    print_good("#{domain} A: #{r.address} ") if displayed
  end
  return if records.blank?
  dns_note(domain, type, records) if datastore['DnsNote']
  records
end

#dns_get_cname(domain) ⇒ Object


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 123

def dns_get_cname(domain)
  print_status("Querying DNS CNAME records for #{domain}")
  resp = dns_query(domain, 'CNAME')
  return if resp.blank? || resp.answer.blank?

  records = []
  resp.answer.each do |r|
    next unless r.class == Dnsruby::RR::IN::CNAME
    records << r.cname.to_s
    print_good("#{domain} CNAME: #{r.cname}")
  end
  return if records.blank?
  dns_note(domain, 'DNS CNAME records', records) if datastore['DnsNote']
  records
end

#dns_get_mx(domain) ⇒ Object


139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 139

def dns_get_mx(domain)
  print_status("Querying DNS MX records for #{domain}")
  begin
    resp = dns_query(domain, 'MX')
    return if resp.blank? || resp.answer.blank?

    records = []
    resp.answer.each do |r|
      next unless r.class == Dnsruby::RR::IN::MX
      records << r.exchange.to_s
      print_good("#{domain} MX: #{r.exchange}")
    end
  rescue SocketError => e
    print_error("Query #{domain} DNS MX - exception: #{e}")
  ensure
    return if records.blank?
    dns_note(domain, 'DNS MX records', records) if datastore['DnsNote']
    records
  end
end

#dns_get_ns(domain) ⇒ Object


160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 160

def dns_get_ns(domain)
  print_status("Querying DNS NS records for #{domain}")
  resp = dns_query(domain, 'NS')
  return if resp.blank? || resp.answer.blank?

  records = []
  resp.answer.each do |r|
    next unless r.class == Dnsruby::RR::IN::NS
    records << r.nsdname.to_s
    print_good("#{domain} NS: #{r.nsdname}")
  end
  return if records.blank?
  dns_note(domain, 'DNS NS records', records) if datastore['DnsNote']
  records
end

#dns_get_ptr(ip) ⇒ Object


176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 176

def dns_get_ptr(ip)
  resp = dns_query(ip, nil)
  return if resp.blank? || resp.answer.blank?

  records = []
  resp.answer.each do |r|
    next unless r.class == Dnsruby::RR::IN::PTR
    records << r.ptr.to_s
    print_good("#{ip}: PTR: #{r.ptr} ")
  end
  return if records.blank?
  dns_note(ip, 'DNS PTR records', records) if datastore['DnsNote']
  records
end

#dns_get_soa(domain) ⇒ Object


191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 191

def dns_get_soa(domain)
  print_status("Querying DNS SOA records for #{domain}")
  resp = dns_query(domain, 'SOA')
  return if resp.blank? || resp.answer.blank?

  records = []
  resp.answer.each do |r|
    next unless r.class == Dnsruby::RR::IN::SOA
    records << r.mname.to_s
    print_good("#{domain} SOA: #{r.mname}")
  end
  return if records.blank?
  dns_note(domain, 'DNS SOA records', records) if datastore['DnsNote']
  records
end

#dns_get_srv(domain) ⇒ Object


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 207

def dns_get_srv(domain)
  print_status("Querying DNS SRV records for #{domain}")
  srv_protos = %w(tcp udp tls)
  srv_record_types = %w(
    gc kerberos ldap test sips sip aix finger ftp http
    nntp telnet whois h323cs h323be h323ls sipinternal sipinternaltls
    sipfederationtls jabber jabber-client jabber-server xmpp-server xmpp-client
    imap certificates crls pgpkeys pgprevokations cmp svcp crl oscp pkixrep
    smtp hkp hkps)

  srv_records_data = []
  srv_record_types.each do |srv_record_type|
    srv_protos.each do |srv_proto|
      srv_record = "_#{srv_record_type}._#{srv_proto}.#{domain}"
      resp = dns_query(srv_record, Net::DNS::SRV)
      next if resp.blank? || resp.answer.blank?
      srv_record_data = []
      resp.answer.each do |r|
        next if r.class == Dnsruby::RR::IN::CNAME
        data = {
          host: r.name.to_s,
          port: r.port,
          priority: r.priority
        }
        print_good("#{srv_record} SRV: #{data}")
        srv_record_data << data
      end
      if datastore['DnsNote']
        srv_records_data << {
          srv_record => srv_record_data
        }
        report_note(
          type: srv_record,
          data: srv_record_data
        )
      end
    end
  end
  return if srv_records_data.empty?
end

#dns_get_tld(domain) ⇒ Object


249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 249

def dns_get_tld(domain)
  print_status("Querying DNS TLD records for #{domain}")
  domain_ = domain.split('.')
  domain_.pop
  domain_ = domain_.join('.')

  tlds = [
    'com', 'org', 'net', 'edu', 'mil', 'gov', 'uk', 'af', 'al', 'dz',
    'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'ac', 'au',
    'at', 'az', 'bs', 'bh', 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm',
    'bt', 'bo', 'ba', 'bw', 'bv', 'br', 'io', 'bn', 'bg', 'bf', 'bi',
    'kh', 'cm', 'ca', 'cv', 'ky', 'cf', 'td', 'cl', 'cn', 'cx', 'cc',
    'co', 'km', 'cd', 'cg', 'ck', 'cr', 'ci', 'hr', 'cu', 'cy', 'cz',
    'dk', 'dj', 'dm', 'do', 'tp', 'ec', 'eg', 'sv', 'gq', 'er', 'ee',
    'et', 'fk', 'fo', 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm',
    'ge', 'de', 'gh', 'gi', 'gr', 'gl', 'gd', 'gp', 'gu', 'gt', 'gg',
    'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', 'in',
    'id', 'ir', 'iq', 'ie', 'im', 'il', 'it', 'jm', 'jp', 'je', 'jo',
    'kz', 'ke', 'ki', 'kp', 'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls',
    'lr', 'ly', 'li', 'lt', 'lu', 'mo', 'mk', 'mg', 'mw', 'my', 'mv',
    'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx', 'fm', 'md', 'mc',
    'mn', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'an', 'nc',
    'nz', 'ni', 'ne', 'ng', 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw',
    'pa', 'pg', 'py', 'pe', 'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 're',
    'ro', 'ru', 'rw', 'kn', 'lc', 'vc', 'ws', 'sm', 'st', 'sa', 'sn',
    'sc', 'sl', 'sg', 'sk', 'si', 'sb', 'so', 'za', 'gz', 'es', 'lk',
    'sh', 'pm', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 'sy', 'tw', 'tj',
    'tz', 'th', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv',
    'ug', 'ua', 'ae', 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn',
    'vg', 'vi', 'wf', 'eh', 'ye', 'yu', 'za', 'zr', 'zm', 'zw', 'int',
    'gs', 'info', 'biz', 'su', 'name', 'coop', 'aero'
  ]

  records = []
  begin
    tlds.each do |tld|
      tldr = dns_get_a("#{domain_}.#{tld}", 'DNS TLD records')
      next if tldr.nil?
      records |= tldr
      print_good("#{domain_}.#{tld}: TLD: #{tldr.join(',')}")
    end
  rescue ArgumentError => e
    print_error("Query #{domain} DNS TLD - exception: #{e}")
  ensure
    records
  end
end

#dns_get_txt(domain) ⇒ Object


297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 297

def dns_get_txt(domain)
  print_status("Querying DNS TXT records for #{domain}")
  resp = dns_query(domain, 'TXT')
  return if resp.blank? || resp.answer.blank?

  records = []
  resp.answer.each do |r|
    next unless r.class == Dnsruby::RR::IN::TXT
    txt = r.strings.join(' ')
    records << txt
    print_good("#{domain} TXT: #{txt}")
  end
  return if records.blank?
  dns_note(domain, 'DNS TXT records', records) if datastore['DnsNote']
  records
end

#dns_note(target, type, records) ⇒ Object


314
315
316
317
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 314

def dns_note(target, type, records)
  data = { 'target' => target, 'records' => records }
  report_note(host: target, sname: 'dns', type: type, data: data, update: :unique_data)
end

#dns_reverse(cidr, threads) ⇒ Object


319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 319

def dns_reverse(cidr, threads)
  unless cidr
    print_error 'ENUM_RVL enabled, but no IPRANGE specified'
    return
  end

  iplst = []
  ipadd = Rex::Socket::RangeWalker.new(cidr)
  numip = ipadd.num_ips
  while iplst.length < numip
    ipa = ipadd.next_ip
    break unless ipa
    iplst << ipa
  end

  records = []
  while !iplst.nil? && !iplst.empty?
    t = []
    threads = 1 if threads <= 0
    begin
      1.upto(threads) do
        t << framework.threads.spawn("Module(#{refname})", false, iplst.shift) do |ip_text|
          next if ip_text.nil?
          a = dns_get_ptr(ip_text)
          records |= a if a
        end
      end
      t.map(&:join)

    rescue ::Timeout::Error
    ensure
      t.each { |x| x.kill rescue nil }
    end
  end
  records
end

#dns_wildcard_enabled?(domain) ⇒ Boolean

Returns:

  • (Boolean)

356
357
358
359
360
361
362
363
364
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 356

def dns_wildcard_enabled?(domain)
  records = dns_get_a("#{Rex::Text.rand_text_alpha(16)}.#{domain}", 'DNS wildcard records')
  if records.blank?
    false
  else
    print_warning('dns wildcard is enable OR fake dns server')
    true
  end
end

#initialize(info = {}) ⇒ Object


17
18
19
20
21
22
23
24
25
26
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 17

def initialize(info = {})
  super

  register_advanced_options(
    [
      OptBool.new('DnsNote', [false, 'Save all DNS results as notes', true]),
      OptInt.new('DnsClientUdpTimeout', [true, 'Number of seconds to wait for a response to a UDP query', 8])
    ]
  )
end

#setup_resolverObject


30
31
32
33
34
# File 'lib/msf/core/exploit/dns/enumeration.rb', line 30

def setup_resolver
  dns_resolver = super
  dns_resolver.udp_timeout = datastore['DnsClientUdpTimeout']
  @dns_resolver = dns_resolver
end