Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/command_dispatcher/dns.rb
19664 views
1
# -*- coding: binary -*-
2
3
module Msf
4
module Ui
5
module Console
6
module CommandDispatcher
7
8
class DNS
9
10
include Msf::Ui::Console::CommandDispatcher
11
12
ADD_USAGE = 'dns [add] [--index <insertion index>] [--rule <wildcard DNS entry>] [--session <session id>] <resolver> ...'.freeze
13
@@add_opts = Rex::Parser::Arguments.new(
14
['-i', '--index'] => [true, 'Index to insert at'],
15
['-r', '--rule'] => [true, 'Set a DNS wildcard entry to match against'],
16
['-s', '--session'] => [true, 'Force the DNS request to occur over a particular channel (override routing rules)']
17
)
18
19
ADD_STATIC_USAGE = 'dns [add-static] <hostname> <IP address> ...'.freeze
20
21
REMOVE_USAGE = 'dns [remove/del] -i <entry id> [-i <entry id> ...]'.freeze
22
@@remove_opts = Rex::Parser::Arguments.new(
23
['-i', '--index'] => [true, 'Index to remove at']
24
)
25
26
REMOVE_STATIC_USAGE = 'dns [remove-static] <hostname> [<IP address> ...]'.freeze
27
28
RESET_CONFIG_USAGE = 'dns [reset-config] [-y/--yes] [--system]'.freeze
29
@@reset_config_opts = Rex::Parser::Arguments.new(
30
['-y', '--yes'] => [false, 'Assume yes and do not prompt for confirmation before resetting'],
31
['--system'] => [false, 'Include the system resolver']
32
)
33
34
RESOLVE_USAGE = 'dns [resolve] [-f <address family>] <hostname> ...'.freeze
35
@@resolve_opts = Rex::Parser::Arguments.new(
36
# same usage syntax as Rex::Post::Meterpreter::Ui::Console::CommandDispatcher::Stdapi
37
['-f'] => [true, 'Address family - IPv4 or IPv6 (default IPv4)']
38
)
39
40
REORDER_USAGE = 'dns [reorder] -i <existing ID> <new ID>'.freeze
41
@@reorder_opts = Rex::Parser::Arguments.new(
42
['-i', '--index'] => [true, 'Index of the rule to move']
43
)
44
45
def initialize(driver)
46
super
47
end
48
49
def name
50
'DNS'
51
end
52
53
def commands
54
commands = {}
55
56
if framework.features.enabled?(Msf::FeatureManager::DNS)
57
commands = {
58
'dns' => "Manage Metasploit's DNS resolving behaviour"
59
}
60
end
61
commands
62
end
63
64
#
65
# Tab completion for the dns command
66
#
67
# @param str [String] the string currently being typed before tab was hit
68
# @param words [Array<String>] the previously completed words on the command line. The array
69
# contains at least one entry when tab completion has reached this stage since the command itself has been completed
70
def cmd_dns_tabs(str, words)
71
return if driver.framework.dns_resolver.nil?
72
73
subcommands = %w[ add add-static delete flush-cache flush-entries flush-static help print query remove remove-static reset-config resolve ]
74
if words.length == 1
75
return subcommands.select { |opt| opt.start_with?(str) }
76
end
77
78
cmd = words[1]
79
case cmd
80
when 'add'
81
# We expect a repeating pattern of tag (e.g. -r) and then a value (e.g. *.metasploit.com)
82
# Once this pattern is violated, we're just specifying DNS servers at that point.
83
tag_is_expected = true
84
if words.length > 2
85
words[2..-1].each do |word|
86
if tag_is_expected && !word.start_with?('-')
87
return
88
end
89
tag_is_expected = !tag_is_expected
90
end
91
end
92
93
case words[-1]
94
when '-r', '--rule'
95
# Hard to auto-complete a rule with any meaningful value; just return
96
return
97
when '-s', '--session'
98
session_ids = driver.framework.sessions.keys.map { |k| k.to_s }
99
return session_ids.select { |id| id.start_with?(str) }
100
when /^-/
101
# Unknown tag
102
return
103
end
104
105
options = @@add_opts.option_keys.select { |opt| opt.start_with?(str) }
106
options << '' # Prevent tab-completion of a dash, given they could provide an IP address at this point
107
return options
108
when 'add-static'
109
if words.length == 2
110
# tab complete existing hostnames because they can have more than one IP address
111
return resolver.static_hostnames.each.select { |hostname,_| hostname.downcase.start_with?(str.downcase) }.map { |hostname,_| hostname }
112
end
113
when 'help'
114
# These commands don't have any arguments
115
return subcommands.select { |sc| sc.start_with?(str) }
116
when 'remove','delete','reorder'
117
if words[-1] == '-i'
118
return
119
else
120
return @@remove_opts.option_keys.select { |opt| opt.start_with?(str) }
121
end
122
when 'remove-static'
123
if words.length == 2
124
return resolver.static_hostnames.each.select { |hostname,_| hostname.downcase.start_with?(str.downcase) }.map { |hostname,_| hostname }
125
elsif words.length > 2
126
hostname = words[2]
127
ip_addresses = resolver.static_hostnames.get(hostname, Dnsruby::Types::A) + resolver.static_hostnames.get(hostname, Dnsruby::Types::AAAA)
128
return ip_addresses.map(&:to_s).select { |ip_address| ip_address.start_with?(str) }
129
end
130
when 'reset-config'
131
@@reset_config_opts.option_keys.select { |opt| opt.start_with?(str) }
132
when 'resolve','query'
133
if words[-1] == '-f'
134
families = %w[ IPv4 IPv6 ] # The family argument is case-insensitive
135
return families.select { |family| family.downcase.start_with?(str.downcase) }
136
else
137
@@resolve_opts.option_keys.select { |opt| opt.start_with?(str) }
138
end
139
end
140
end
141
142
def cmd_dns_help(*args)
143
if args.first.present?
144
handler = "#{args.first.gsub('-', '_')}_dns"
145
if respond_to?("#{handler}_help")
146
# if it is a valid command with dedicated help information
147
return send("#{handler}_help")
148
elsif respond_to?(handler)
149
# if it is a valid command without dedicated help information
150
print_error("No help menu is available for #{args.first}")
151
return
152
else
153
print_error("Invalid subcommand: #{args.first}")
154
end
155
end
156
157
print_line "Manage Metasploit's DNS resolution behaviour"
158
print_line
159
print_line "USAGE:"
160
print_line " #{ADD_USAGE}"
161
print_line " #{ADD_STATIC_USAGE}"
162
print_line " #{REMOVE_USAGE}"
163
print_line " #{REMOVE_STATIC_USAGE}"
164
print_line " #{REORDER_USAGE}"
165
print_line " dns [flush-cache]"
166
print_line " dns [flush-entries]"
167
print_line " dns [flush-static]"
168
print_line " dns [print]"
169
print_line " #{RESET_CONFIG_USAGE}"
170
print_line " #{RESOLVE_USAGE}"
171
print_line " dns [help] [subcommand]"
172
print_line
173
print_line "SUBCOMMANDS:"
174
print_line " add - Add a DNS resolution entry to resolve certain domain names through a particular DNS resolver"
175
print_line " add-static - Add a statically defined hostname"
176
print_line " flush-cache - Remove all cached DNS answers"
177
print_line " flush-entries - Remove all configured DNS resolution entries"
178
print_line " flush-static - Remove all statically defined hostnames"
179
print_line " print - Show all configured DNS resolution entries"
180
print_line " remove - Delete one or more DNS resolution entries"
181
print_line " remove-static - Delete a statically defined hostname"
182
print_line " reset-config - Reset the DNS configuration"
183
print_line " resolve - Resolve a hostname"
184
print_line " reorder - Reorder one or more rules"
185
print_line
186
print_line "EXAMPLES:"
187
print_line " Display help information for the 'add' subcommand"
188
print_line " dns help add"
189
print_line
190
end
191
192
#
193
# Manage Metasploit's DNS resolution rules
194
#
195
def cmd_dns(*args)
196
if driver.framework.dns_resolver.nil?
197
print_warning("Run the #{Msf::Ui::Tip.highlight("save")} command and restart the console for this feature configuration to take effect.")
198
return
199
end
200
201
args << 'print' if args.length == 0
202
# Short-circuit help
203
if args.delete("-h") || args.delete("--help")
204
subcommand = args.first
205
if subcommand && respond_to?("#{subcommand.gsub('-', '_')}_dns_help")
206
# if it is a valid command with dedicated help information
207
send("#{subcommand.gsub('-', '_')}_dns_help")
208
else
209
# otherwise print the top-level help information
210
cmd_dns_help
211
end
212
return
213
end
214
215
action = args.shift
216
begin
217
case action
218
when "add"
219
add_dns(*args)
220
when "add-static"
221
add_static_dns(*args)
222
when "flush-entries"
223
flush_entries_dns
224
when "flush-cache"
225
flush_cache_dns
226
when "flush-static"
227
flush_static_dns
228
when "help"
229
cmd_dns_help(*args)
230
when "print"
231
print_dns
232
when 'reorder'
233
reorder_dns(*args)
234
when "remove", "rm", "delete", "del"
235
remove_dns(*args)
236
when "remove-static"
237
remove_static_dns(*args)
238
when "reset-config"
239
reset_config_dns(*args)
240
when "resolve", "query"
241
resolve_dns(*args)
242
else
243
print_error("Invalid command. To view help: dns -h")
244
end
245
rescue ::ArgumentError => e
246
print_error(e.message)
247
end
248
end
249
250
def add_dns(*args)
251
rules = ['*']
252
first_rule = true
253
comm = nil
254
resolvers = []
255
index = -1
256
@@add_opts.parse(args) do |opt, idx, val|
257
unless resolvers.empty? || opt.nil?
258
raise ::ArgumentError.new("Invalid command near #{opt}")
259
end
260
case opt
261
when '-i', '--index'
262
raise ::ArgumentError.new("Not a valid index: #{val}") unless val.to_i > 0
263
264
index = val.to_i - 1
265
when '-r', '--rule'
266
raise ::ArgumentError.new('No rule specified') if val.nil?
267
268
rules.clear if first_rule # if the user defines even one rule, clear the defaults
269
first_rule = false
270
rules << val
271
when '-s', '--session'
272
if val.nil?
273
raise ::ArgumentError.new('No session specified')
274
end
275
276
unless comm.nil?
277
raise ::ArgumentError.new('Only one session can be specified')
278
end
279
280
comm = val
281
when nil
282
val = 'black-hole' if val.casecmp?('blackhole')
283
resolvers << val
284
else
285
raise ::ArgumentError.new("Unknown flag: #{opt}")
286
end
287
end
288
289
# The remaining args should be the DNS servers
290
if resolvers.length < 1
291
raise ::ArgumentError.new('You must specify at least one upstream DNS resolver')
292
end
293
294
resolvers.each do |resolver|
295
unless Rex::Proto::DNS::UpstreamRule.valid_resolver?(resolver)
296
message = "Invalid DNS resolver: #{resolver}."
297
if (suggestions = Rex::Proto::DNS::UpstreamRule.spell_check_resolver(resolver)).present?
298
message << " Did you mean #{suggestions.first}?"
299
end
300
301
raise ::ArgumentError.new(message)
302
end
303
end
304
305
comm_obj = nil
306
307
unless comm.nil?
308
raise ::ArgumentError.new("Not a valid session: #{comm}") unless comm =~ /\A-?[0-9]+\Z/
309
310
comm_obj = driver.framework.sessions.get(comm.to_i)
311
raise ::ArgumentError.new("Session does not exist: #{comm}") unless comm_obj
312
raise ::ArgumentError.new("Socket Comm (Session #{comm}) does not implement Rex::Socket::Comm") unless comm_obj.is_a? ::Rex::Socket::Comm
313
314
if resolvers.any? { |resolver| SPECIAL_RESOLVERS.include?(resolver.downcase) }
315
print_warning("The session argument will be ignored for the system resolver")
316
end
317
end
318
319
rules.each_with_index do |rule, offset|
320
print_warning("DNS rule #{rule} does not contain wildcards, it will not match subdomains") unless rule.include?('*')
321
driver.framework.dns_resolver.add_upstream_rule(
322
resolvers,
323
comm: comm_obj,
324
wildcard: rule,
325
index: (index == -1 ? -1 : offset + index)
326
)
327
end
328
329
print_good("#{rules.length} DNS #{rules.length > 1 ? 'entries' : 'entry'} added")
330
end
331
332
def add_dns_help
333
print_line "USAGE:"
334
print_line " #{ADD_USAGE}"
335
print_line @@add_opts.usage
336
print_line "RESOLVERS:"
337
print_line " ipv4 / ipv6 address - The IP address of an upstream DNS server to resolve from"
338
print_line " #{Rex::Proto::DNS::UpstreamResolver::Type::BLACK_HOLE.to_s.ljust(19)} - Drop all queries"
339
print_line " #{Rex::Proto::DNS::UpstreamResolver::Type::STATIC.to_s.ljust(19) } - Reply with statically configured addresses (only for A/AAAA records)"
340
print_line " #{Rex::Proto::DNS::UpstreamResolver::Type::SYSTEM.to_s.ljust(19) } - Use the host operating systems DNS resolution functionality (only for A/AAAA records)"
341
print_line
342
print_line "EXAMPLES:"
343
print_line " Set the DNS server(s) to be used for *.metasploit.com to 192.168.1.10"
344
print_line " dns add --rule *.metasploit.com 192.168.1.10"
345
print_line
346
print_line " Add multiple entries at once"
347
print_line " dns add --rule *.metasploit.com --rule *.google.com 192.168.1.10 192.168.1.11"
348
print_line
349
print_line " Set the DNS server(s) to be used for *.metasploit.com to 192.168.1.10, but specifically to go through session 2"
350
print_line " dns add --session 2 --rule *.metasploit.com 192.168.1.10"
351
end
352
353
def add_static_dns(*args)
354
if args.length < 2
355
raise ::ArgumentError.new('A hostname and IP address must be provided')
356
end
357
358
hostname = args.shift
359
if !Rex::Proto::DNS::StaticHostnames.is_valid_hostname?(hostname)
360
raise ::ArgumentError.new("Invalid hostname: #{hostname}")
361
end
362
363
ip_addresses = args
364
if (ip_address = ip_addresses.find { |a| !Rex::Socket.is_ip_addr?(a) })
365
raise ::ArgumentError.new("Invalid IP address: #{ip_address}")
366
end
367
368
ip_addresses.each do |ip_address|
369
resolver.static_hostnames.add(hostname, ip_address)
370
print_status("Added static hostname mapping #{hostname} to #{ip_address}")
371
end
372
end
373
374
def add_static_dns_help
375
print_line "USAGE:"
376
print_line " #{ADD_STATIC_USAGE}"
377
print_line
378
print_line "EXAMPLES:"
379
print_line " Define a static entry mapping localhost6 to ::1"
380
print_line " dns add-static localhost6 ::1"
381
end
382
383
#
384
# Query a hostname using the configuration. This is useful for debugging and
385
# inspecting the active settings.
386
#
387
def resolve_dns(*args)
388
names = []
389
query_type = Dnsruby::Types::A
390
391
@@resolve_opts.parse(args) do |opt, idx, val|
392
unless names.empty? || opt.nil?
393
raise ::ArgumentError.new("Invalid command near #{opt}")
394
end
395
case opt
396
when '-f'
397
case val.downcase
398
when 'ipv4'
399
query_type = Dnsruby::Types::A
400
when'ipv6'
401
query_type = Dnsruby::Types::AAAA
402
else
403
raise ::ArgumentError.new("Invalid family: #{val}")
404
end
405
when nil
406
names << val
407
else
408
raise ::ArgumentError.new("Unknown flag: #{opt}")
409
end
410
end
411
412
if names.length < 1
413
raise ::ArgumentError.new('You must specify at least one hostname to resolve')
414
end
415
416
tbl = Table.new(
417
Table::Style::Default,
418
'Header' => 'Host resolutions',
419
'Prefix' => "\n",
420
'Postfix' => "\n",
421
'Columns' => ['Hostname', 'IP Address', 'Rule #', 'Rule', 'Resolver', 'Comm channel'],
422
'ColProps' => { 'Hostname' => { 'Strip' => false } },
423
'SortIndex' => -1,
424
'WordWrap' => false
425
)
426
names.each do |name|
427
upstream_rule = resolver.upstream_rules.find { |ur| ur.matches_name?(name) }
428
if upstream_rule.nil?
429
tbl << [name, '[Failed To Resolve]', '', '', '', '']
430
next
431
end
432
433
upstream_rule_idx = resolver.upstream_rules.index(upstream_rule) + 1
434
435
begin
436
result = resolver.query(name, query_type)
437
rescue NoResponseError
438
tbl = append_resolver_cells!(tbl, upstream_rule, prefix: [name, '[Failed To Resolve]'], index: upstream_rule_idx)
439
else
440
if result.answer.empty?
441
tbl = append_resolver_cells!(tbl, upstream_rule, prefix: [name, '[Failed To Resolve]'], index: upstream_rule_idx)
442
else
443
result.answer.select do |answer|
444
answer.type == query_type
445
end.map(&:address).map(&:to_s).each do |address|
446
tbl = append_resolver_cells!(tbl, upstream_rule, prefix: [name, address], index: upstream_rule_idx)
447
end
448
end
449
end
450
end
451
print(tbl.to_s)
452
end
453
454
def resolve_dns_help
455
print_line "USAGE:"
456
print_line " #{RESOLVE_USAGE}"
457
print_line @@resolve_opts.usage
458
print_line "EXAMPLES:"
459
print_line " Resolve a hostname to an IPv6 address using the current configuration"
460
print_line " dns resolve -f IPv6 www.metasploit.com"
461
print_line
462
end
463
464
#
465
# Remove all matching user-configured DNS entries
466
#
467
def remove_dns(*args)
468
remove_ids = []
469
@@remove_opts.parse(args) do |opt, idx, val|
470
case opt
471
when '-i', '--index'
472
raise ::ArgumentError.new("Not a valid index: #{val}") unless val.to_i > 0
473
474
remove_ids << val.to_i - 1
475
end
476
end
477
478
if remove_ids.empty?
479
raise ::ArgumentError.new('At least one index to remove must be provided')
480
end
481
482
removed = resolver.remove_ids(remove_ids)
483
print_warning('Some entries were not removed') unless removed.length == remove_ids.length
484
if removed.length > 0
485
print_good("#{removed.length} DNS #{removed.length > 1 ? 'entries' : 'entry'} removed")
486
print_dns_set('Deleted entries', removed, ids: [nil] * removed.length)
487
end
488
end
489
490
def remove_dns_help
491
print_line "USAGE:"
492
print_line " #{REMOVE_USAGE}"
493
print_line(@@remove_opts.usage)
494
print_line "EXAMPLES:"
495
print_line " Delete the DNS resolution rule #3"
496
print_line " dns remove -i 3"
497
print_line
498
print_line " Delete multiple rules in one command"
499
print_line " dns remove -i 3 -i 4 -i 5"
500
print_line
501
end
502
503
def reorder_dns(*args)
504
reorder_ids = []
505
new_id = -1
506
@@remove_opts.parse(args) do |opt, idx, val|
507
case opt
508
when '-i', '--index'
509
raise ::ArgumentError.new("Not a valid index: #{val}") unless val.to_i > 0
510
raise ::ArgumentError.new("Duplicate index: #{val}") if reorder_ids.include?(val.to_i - 1)
511
512
reorder_ids << val.to_i - 1
513
when nil
514
raise ::ArgumentError.new("Not a valid index: #{val}") unless (val.to_i > 0 || val.to_i == -1)
515
new_id = val.to_i
516
new_id -= 1 unless new_id == -1
517
end
518
end
519
520
if reorder_ids.empty?
521
raise ::ArgumentError.new('At least one index to reorder must be provided')
522
end
523
524
reordered = resolver.reorder_ids(reorder_ids, new_id)
525
print_warning('Some entries were not reordered') unless reordered.length == reorder_ids.length
526
if reordered.length > 0
527
print_good("#{reordered.length} DNS #{reordered.length > 1 ? 'entries' : 'entry'} reordered")
528
print_resolver_rules
529
end
530
end
531
532
def reorder_dns_help
533
print_line "USAGE:"
534
print_line " #{REORDER_USAGE}"
535
print_line "If providing multiple IDs, they will be inserted at the given index in the order you provide."
536
print_line(@@reorder_opts.usage)
537
print_line "EXAMPLES:"
538
print_line " Move the third DNS entry to the top of the resolution order"
539
print_line " dns reorder -i 3 1"
540
print_line
541
print_line " Move the third and fifth DNS entries just below the first entry (i.e. becoming the second and third entries, respectively)"
542
print_line " dns reorder -i 3 -i 5 2"
543
print_line
544
print_line " Move the second and third DNS entries to the bottom of the resolution order"
545
print_line " dns reorder -i 2 -i 3 -1"
546
print_line " Alternatively, assuming there are 6 entries in the list"
547
print_line " dns reorder -i 2 -i 3 7"
548
print_line
549
end
550
551
def remove_static_dns(*args)
552
if args.length < 1
553
raise ::ArgumentError.new('A hostname must be provided')
554
end
555
556
hostname = args.shift
557
if !Rex::Proto::DNS::StaticHostnames.is_valid_hostname?(hostname)
558
raise ::ArgumentError.new("Invalid hostname: #{hostname}")
559
end
560
561
ip_addresses = args
562
if ip_addresses.empty?
563
ip_addresses = resolver.static_hostnames.get(hostname, Dnsruby::Types::A) + resolver.static_hostnames.get(hostname, Dnsruby::Types::AAAA)
564
if ip_addresses.empty?
565
print_status("There are no definitions for hostname: #{hostname}")
566
end
567
elsif (ip_address = ip_addresses.find { |ip| !Rex::Socket.is_ip_addr?(ip) })
568
raise ::ArgumentError.new("Invalid IP address: #{ip_address}")
569
end
570
571
ip_addresses.each do |ip_address|
572
resolver.static_hostnames.delete(hostname, ip_address)
573
print_status("Removed static hostname mapping #{hostname} to #{ip_address}")
574
end
575
end
576
577
def remove_static_dns_help
578
print_line "USAGE:"
579
print_line " #{REMOVE_STATIC_USAGE}"
580
print_line
581
print_line "EXAMPLES:"
582
print_line " Remove all IPv4 and IPv6 addresses for 'localhost'"
583
print_line " dns remove-static localhost"
584
print_line
585
end
586
587
def reset_config_dns(*args)
588
add_system_resolver = false
589
should_confirm = true
590
@@reset_config_opts.parse(args) do |opt, idx, val|
591
case opt
592
when '--system'
593
add_system_resolver = true
594
when '-y', '--yes'
595
should_confirm = false
596
end
597
end
598
599
if should_confirm
600
print("Are you sure you want to reset the DNS configuration? [y/N]: ")
601
response = gets.downcase.chomp
602
return unless response =~ /^y/i
603
end
604
605
resolver.reinit
606
print_status('The DNS configuration has been reset')
607
608
if add_system_resolver
609
# if the user requested that we add the system resolver
610
system_resolver = Rex::Proto::DNS::UpstreamResolver.create_system
611
# first find the default, catch-all rule
612
default_rule = resolver.upstream_rules.find { |ur| ur.matches_all? }
613
if default_rule.nil?
614
resolver.add_upstream_rule([ system_resolver ])
615
else
616
# if the first resolver is for static hostnames, insert after that one
617
if default_rule.resolvers.first&.type == Rex::Proto::DNS::UpstreamResolver::Type::STATIC
618
index = 1
619
else
620
index = 0
621
end
622
default_rule.resolvers.insert(index, system_resolver)
623
end
624
end
625
626
print_dns
627
628
if ENV['PROXYCHAINS_CONF_FILE'] && !add_system_resolver
629
print_warning('Detected proxychains but the system resolver was not added')
630
end
631
end
632
633
def reset_config_dns_help
634
print_line "USAGE:"
635
print_line " #{RESET_CONFIG_USAGE}"
636
print_line @@reset_config_opts.usage
637
print_line "EXAMPLES:"
638
print_line " Reset the configuration without prompting to confirm"
639
print_line " dns reset-config --yes"
640
print_line
641
end
642
643
#
644
# Delete all cached DNS answers
645
#
646
def flush_cache_dns
647
resolver.cache.flush
648
print_good('DNS cache flushed')
649
end
650
651
#
652
# Delete all user-configured DNS settings
653
#
654
def flush_entries_dns
655
resolver.flush
656
print_good('DNS entries flushed')
657
end
658
659
def flush_static_dns
660
resolver.static_hostnames.flush
661
print_good('DNS static hostnames flushed')
662
end
663
664
def print_resolver_rules
665
upstream_rules = resolver.upstream_rules
666
print_dns_set('Resolver rule entries', upstream_rules, ids: (1..upstream_rules.length).to_a)
667
if upstream_rules.empty?
668
print_line
669
print_error('No DNS nameserver entries configured')
670
end
671
end
672
673
#
674
# Display the user-configured DNS settings
675
#
676
def print_dns
677
default_domain = 'N/A'
678
if resolver.defname? && resolver.domain.present?
679
default_domain = resolver.domain
680
end
681
print_line("Default search domain: #{default_domain}")
682
683
searchlist = resolver.searchlist
684
case searchlist.length
685
when 0
686
print_line('Default search list: N/A')
687
when 1
688
print_line("Default search list: #{searchlist.first}")
689
else
690
print_line('Default search list:')
691
searchlist.each do |entry|
692
print_line(" * #{entry}")
693
end
694
end
695
print_line("Current cache size: #{resolver.cache.records.length}")
696
697
print_resolver_rules
698
699
tbl = Table.new(
700
Table::Style::Default,
701
'Header' => 'Static hostnames',
702
'Prefix' => "\n",
703
'Postfix' => "\n",
704
'Columns' => ['Hostname', 'IPv4 Address', 'IPv6 Address'],
705
'ColProps' => { 'Hostname' => { 'Strip' => false } },
706
'SortIndex' => -1,
707
'WordWrap' => false
708
)
709
resolver.static_hostnames.sort_by { |hostname, _| hostname }.each do |hostname, addresses|
710
ipv4_addresses = addresses.fetch(Dnsruby::Types::A, []).sort_by(&:to_i)
711
ipv6_addresses = addresses.fetch(Dnsruby::Types::AAAA, []).sort_by(&:to_i)
712
if (ipv4_addresses.length <= 1 && ipv6_addresses.length <= 1) && ((ipv4_addresses + ipv6_addresses).length > 0)
713
tbl << [hostname, ipv4_addresses.first, ipv6_addresses.first]
714
else
715
tbl << [hostname, '', '']
716
0.upto([ipv4_addresses.length, ipv6_addresses.length].max - 1) do |idx|
717
tbl << [TABLE_INDENT, ipv4_addresses[idx], ipv6_addresses[idx]]
718
end
719
end
720
end
721
print_line(tbl.to_s)
722
if resolver.static_hostnames.empty?
723
print_line('No static hostname entries are configured')
724
end
725
end
726
727
private
728
729
SPECIAL_RESOLVERS = [
730
Rex::Proto::DNS::UpstreamResolver::Type::BLACK_HOLE.to_s.downcase,
731
Rex::Proto::DNS::UpstreamResolver::Type::SYSTEM.to_s.downcase
732
].freeze
733
734
TABLE_INDENT = " \\_ ".freeze
735
736
#
737
# Get user-friendly text for displaying the session that this entry would go through
738
#
739
def prettify_comm(comm, upstream_resolver)
740
if !Rex::Socket.is_ip_addr?(upstream_resolver.destination)
741
'N/A'
742
elsif comm.nil?
743
channel = Rex::Socket::SwitchBoard.best_comm(upstream_resolver.destination)
744
if channel.nil?
745
nil
746
else
747
"Session #{channel.sid} (route)"
748
end
749
else
750
if comm.alive?
751
"Session #{comm.sid}"
752
else
753
"Closed session (#{comm.sid})"
754
end
755
end
756
end
757
758
def print_dns_set(heading, result_set, ids: [])
759
return if result_set.length == 0
760
columns = ['#', 'Rule', 'Resolver', 'Comm channel']
761
col_props = { 'Rule' => { 'Strip' => false } }
762
763
tbl = Table.new(
764
Table::Style::Default,
765
'Header' => heading,
766
'Prefix' => "\n",
767
'Postfix' => "\n",
768
'Columns' => columns,
769
'ColProps' => col_props,
770
'SortIndex' => -1,
771
'WordWrap' => false
772
)
773
result_set.each_with_index do |entry, index|
774
tbl = append_resolver_cells!(tbl, entry, index: ids[index])
775
end
776
777
print(tbl.to_s) if tbl.rows.length > 0
778
end
779
780
def append_resolver_cells!(tbl, entry, prefix: [], suffix: [], index: nil)
781
alignment_prefix = prefix.empty? ? [] : (['.'] * prefix.length)
782
783
if entry.resolvers.length == 1
784
tbl << prefix + [index.to_s, entry.wildcard, entry.resolvers.first, prettify_comm(entry.comm, entry.resolvers.first)] + suffix
785
elsif entry.resolvers.length > 1
786
tbl << prefix + [index.to_s, entry.wildcard, '', ''] + suffix
787
entry.resolvers.each do |resolver|
788
tbl << alignment_prefix + ['.', TABLE_INDENT, resolver, prettify_comm(entry.comm, resolver)] + ([''] * suffix.length)
789
end
790
end
791
tbl
792
end
793
794
def resolver
795
self.driver.framework.dns_resolver
796
end
797
end
798
799
end
800
end
801
end
802
end
803
804