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

Includes:
Auxiliary::Report, Client
Defined in:
lib/msf/core/exploit/remote/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, #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



35
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
70
71
72
73
74
75
76
77
78
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 35

def dns_axfr(domain)
  if datastore['NS'].blank?
    nameservers = dns_get_ns(domain)
    nameservers.map! { |ns| Rex::Socket.dotted_ip?(ns) ? ns : dns_get_a(ns, 'DNS AXFR records') }
    nameservers.compact!
    nameservers.flatten!
  else
    nameservers = process_nameservers
  end
  nameservers.uniq!

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

    begin
      dns.nameservers = [ nameserver ]
      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}")
      next
    rescue => e
      print_error("Query #{domain} DNS AXFR - unknown exception: #{e}")
      next
    end

    if zone.blank?
      print_status("Query #{domain} DNS AXFR - no results were received")
      next
    end

    records += zone
    print_good("#{domain} Zone Transfer:")
    print_line(zone.map(&:inspect).join)
  end
  return if records.blank?
  records
end

#dns_bruteforce(domain, wordlist, threads) ⇒ Object



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
107
108
109
110
111
112
113
114
115
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 80

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



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 117

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



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 132

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



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 148

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



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 169

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



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 185

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.rdata.to_s
    print_good("#{ip}: PTR: #{r.rdata} ")
  end
  return if records.blank?
  dns_note(ip, 'DNS PTR records', records) if datastore['DnsNote']
  records
end

#dns_get_soa(domain) ⇒ Object



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

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



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
247
248
249
250
251
252
253
254
255
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 216

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



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
296
297
298
299
300
301
302
303
304
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 258

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



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 306

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



323
324
325
326
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 323

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



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
355
356
357
358
359
360
361
362
363
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 328

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)


365
366
367
368
369
370
371
372
373
# File 'lib/msf/core/exploit/remote/dns/enumeration.rb', line 365

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



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

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



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

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