Skip to content

Commit 1a7eefd

Browse files
committed
Support saving and loading DNS in the MSF config file
1 parent 7442655 commit 1a7eefd

File tree

4 files changed

+97
-9
lines changed

4 files changed

+97
-9
lines changed

lib/msf/core/framework.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def initialize(options={})
9494
def initialize_dns_resolver
9595
self.dns_resolver = Rex::Proto::DNS::CachedResolver.new
9696
self.dns_resolver.extend(Rex::Proto::DNS::CustomNameserverProvider)
97+
self.dns_resolver.load_config
9798
Rex::Socket._install_global_resolver(self.dns_resolver)
9899
end
99100

lib/msf/ui/console/command_dispatcher/core.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,7 @@ def cmd_save(*args)
13501350
# Save the framework's datastore
13511351
begin
13521352
framework.save_config
1353+
driver.framework.dns_resolver.save_config
13531354

13541355
if active_module
13551356
active_module.save_config

lib/msf/ui/console/command_dispatcher/dns.rb

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ def add_dns(*args)
114114
case opt
115115
when '--rule', '-r'
116116
raise ::ArgumentError.new('No rule specified') if val.nil?
117-
raise ::ArgumentError.new("Invalid rule: #{val}") unless valid_rule(val)
118117

119118
rules << val
120119
when '--session', '-s'
@@ -161,14 +160,9 @@ def add_dns(*args)
161160
end
162161
end
163162

164-
#
165-
# Is the given wildcard DNS entry valid?
166-
def valid_rule(rule)
167-
rule =~ /^(\*\.)?([a-z\d][a-z\d-]*[a-z\d]\.)+[a-z]+$/
168-
end
169-
170163
#
171164
# Remove all matching user-configured DNS entries
165+
#
172166
def remove_dns(*args)
173167
remove_ids = []
174168
@@remove_opts.parse(args) do |opt, idx, val|
@@ -184,12 +178,14 @@ def remove_dns(*args)
184178

185179
#
186180
# Delete all user-configured DNS settings
181+
#
187182
def purge_dns
188183
driver.framework.dns_resolver.purge
189184
end
190185

191186
#
192187
# Display the user-configured DNS settings
188+
#
193189
def print_dns
194190
results = driver.framework.dns_resolver.nameserver_entries
195191
columns = ['ID','Rule(s)', 'DNS Server(s)', 'Comm channel']
@@ -204,6 +200,7 @@ def print_dns
204200

205201
#
206202
# Get user-friendly text for displaying the session that this entry would go through
203+
#
207204
def prettify_comm(comm, dns_server)
208205
if comm.nil?
209206
channel = Rex::Socket::SwitchBoard.best_comm(dns_server)
@@ -213,7 +210,7 @@ def prettify_comm(comm, dns_server)
213210
"Session #{channel.sid} (route)"
214211
end
215212
else
216-
if comm.alive
213+
if comm.alive?
217214
"Session #{comm.sid}"
218215
else
219216
"Closed session (#{comm.sid})"

lib/rex/proto/dns/custom_nameserver_provider.rb

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,100 @@ module DNS
77
# for different requests, based on the domain being queried.
88
##
99
module CustomNameserverProvider
10+
CONFIG_KEY = 'framework/dns'
11+
12+
#
13+
# A Comm implementation that always reports as dead, so should never
14+
# be used. This is used to prevent DNS leaks of saved DNS rules that
15+
# were attached to a specific channel.
16+
##
17+
class CommSink
18+
include Msf::Session::Comm
19+
def alive?
20+
false
21+
end
22+
23+
def supports_udp?
24+
# It won't be used anyway, so let's just say we support it
25+
true
26+
end
27+
28+
def sid
29+
'previous MSF session'
30+
end
31+
end
1032

1133
def init
1234
self.entries_with_rules = []
1335
self.entries_without_rules = []
1436
self.next_id = 0
1537
end
1638

39+
def save_config
40+
new_config = {}
41+
[self.entries_with_rules, self.entries_without_rules].each do |entry_set|
42+
entry_set.each do |entry|
43+
key = entry[:id].to_s
44+
val = [entry[:wildcard_rules].join(','),
45+
entry[:dns_server],
46+
(!entry[:comm].nil?).to_s
47+
].join(';')
48+
new_config[key] = val
49+
end
50+
end
51+
52+
Msf::Config.save(CONFIG_KEY => new_config)
53+
end
54+
55+
def load_config
56+
config = Msf::Config.load
57+
58+
with_rules = []
59+
without_rules = []
60+
next_id = 0
61+
62+
dns_settings = config.fetch(CONFIG_KEY, {}).each do |name, value|
63+
id = name.to_i
64+
wildcard_rules, dns_server, uses_comm = value.split(';')
65+
wildcard_rules = wildcard_rules.split(',')
66+
67+
raise Msf::Config::ConfigError.new('DNS parsing failed: Comm must be true or false') unless ['true','false'].include?(uses_comm)
68+
raise Msf::Config::ConfigError.new('Invalid DNS config: Invalid DNS server') unless Rex::Socket.is_ip_addr?(dns_server)
69+
raise Msf::Config::ConfigError.new('Invalid DNS config: Invalid rule') unless wildcard_rules.all? {|rule| valid_rule?(rule)}
70+
71+
comm = uses_comm == 'true' ? CommSink.new : nil
72+
entry = {
73+
:wildcard_rules => wildcard_rules,
74+
:dns_server => dns_server,
75+
:comm => comm,
76+
:id => id
77+
}
78+
79+
if wildcard_rules.empty?
80+
without_rules << entry
81+
else
82+
with_rules << entry
83+
end
84+
85+
next_id = [id, next_id].max
86+
end
87+
88+
# Now that config has successfully read, update the global values
89+
self.entries_with_rules = with_rules
90+
self.entries_without_rules = without_rules
91+
self.next_id = next_id
92+
end
93+
1794
# Add a custom nameserver entry to the custom provider
1895
# @param [wildcard_rules] Array<String> The wildcard rules to match a DNS request against
1996
# @param [dns_server] Array<String> The list of IP addresses that would be used for this custom rule
20-
# @param comm [Integer] The communication channel to be used for these DNS requests
97+
# @param comm [Msf::Session::Comm] The communication channel to be used for these DNS requests
2198
def add_nameserver(wildcard_rules, dns_server, comm)
99+
raise ::ArgumentError.new("Invalid DNS server: #{dns_server}") unless Rex::Socket.is_ip_addr?(dns_server)
100+
wildcard_rules.each do |rule|
101+
raise ::ArgumentError.new("Invalid rule: #{rule}") unless valid_rule?(rule)
102+
end
103+
22104
entry = {
23105
:wildcard_rules => wildcard_rules,
24106
:dns_server => dns_server,
@@ -103,6 +185,13 @@ def self.extended(mod)
103185
end
104186

105187
private
188+
#
189+
# Is the given wildcard DNS entry valid?
190+
#
191+
def valid_rule?(rule)
192+
rule =~ /^(\*\.)?([a-z\d][a-z\d-]*[a-z\d]\.)+[a-z]+$/
193+
end
194+
106195

107196
def matches(domain, pattern)
108197
if pattern.start_with?('*.')

0 commit comments

Comments
 (0)