Skip to content

Commit a7c4b29

Browse files
committed
Register nameserver with framework
1 parent dd209de commit a7c4b29

File tree

4 files changed

+195
-13
lines changed

4 files changed

+195
-13
lines changed

lib/msf/core/framework.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def initialize(options={})
8181
# Configure the SSL certificate generator
8282
require 'msf/core/cert_provider'
8383
Rex::Socket::Ssl.cert_provider = Msf::Ssl::CertProvider
84+
initialize_dns_resolver
8485

8586
subscriber = FrameworkEventSubscriber.new(self)
8687
events.add_exploit_subscriber(subscriber)
@@ -90,6 +91,16 @@ def initialize(options={})
9091
events.add_ui_subscriber(subscriber)
9192
end
9293

94+
def initialize_dns_resolver
95+
self.dns_resolver = Rex::Proto::DNS::CachedResolver.new
96+
self.dns_resolver.extend(Rex::Proto::DNS::CustomNameserverProvider)
97+
Rex::Socket._install_global_resolver(self.dns_resolver)
98+
end
99+
100+
def dns_resolver
101+
self.dns_resolver
102+
end
103+
93104
def inspect
94105
"#<Framework (#{sessions.length} sessions, #{jobs.length} jobs, #{plugins.length} plugins#{db.active ? ", #{db.driver} database active" : ""})>"
95106
end
@@ -147,6 +158,10 @@ def version
147158
Version
148159
end
149160

161+
#
162+
# DNS resolver for the framework
163+
#
164+
attr_reader :dns_resolver
150165
#
151166
# Event management interface for registering event handler subscribers and
152167
# for interacting with the correlation engine.
@@ -278,6 +293,7 @@ def eicar_corrupted?
278293
# @return [Hash]
279294
attr_accessor :options
280295

296+
attr_writer :dns_resolver #:nodoc:
281297
attr_writer :events # :nodoc:
282298
attr_writer :modules # :nodoc:
283299
attr_writer :datastore # :nodoc:

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

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# -*- coding: binary -*-
22

3-
class Msf::Ui::Console::CommandDispatcher::DNS
3+
module Msf
4+
module Ui
5+
module Console
6+
module CommandDispatcher
7+
8+
class DNS
49

510
include Msf::Ui::Console::CommandDispatcher
611

@@ -83,9 +88,9 @@ def cmd_dns(*args)
8388
when "remove", "del"
8489
remove_dns(*args)
8590
when "purge"
86-
purge_dns(*args)
91+
purge_dns
8792
when "print"
88-
print_dns(*args)
93+
print_dns
8994
when "help"
9095
cmd_dns_help
9196
end
@@ -133,19 +138,81 @@ def add_dns(*args)
133138
end
134139

135140
servers.each do |host|
136-
unless host =~ Resolv::IPv4::Regex ||
137-
host =~ Resolv::IPv6::Regex
141+
unless Rex::Socket.is_ip_addr?(host)
138142
raise ::ArgumentError.new("Invalid DNS server: #{host}")
139143
end
140144
end
145+
146+
unless comm.nil?
147+
raise ::ArgumentError.new("Not a valid number: #{comm}") unless comm =~ /^\d+$/
148+
comm_int = comm.to_i
149+
raise ::ArgumentError.new("Session does not exist: #{comm}") unless driver.framework.sessions.include?(comm_int)
150+
151+
end
152+
153+
# Split each DNS server entry up into a separate entry
154+
servers.each do |server|
155+
driver.framework.dns_resolver.add_nameserver(rules, server, comm_int)
156+
end
141157
end
142158

143159
def remove_dns(*args)
144160
end
145161

146-
def purge_dns(*args)
162+
def purge_dns
163+
driver.framework.dns_resolver.purge
147164
end
148165

149-
def print_dns(*args)
166+
def print_dns
167+
results = driver.framework.dns_resolver.nameserver_entries
168+
columns = ['ID','Rule(s)', 'DNS Server(s)', 'Comm channel']
169+
print_dns_set('Custom nameserver rules', columns, results[0].map {|hash| [hash[:id], hash[:wildcard_rules].join(','), hash[:dns_server], prettify_comm(hash[:comm], hash[:dns_server])]})
170+
171+
# Default nameservers don't include a rule
172+
columns = ['ID', 'DNS Server(s)', 'Comm channel']
173+
print_dns_set('Default nameservers', columns, results[1].map {|hash| [hash[:id], hash[:dns_server], prettify_comm(hash[:comm], hash[:dns_server])]})
150174
end
175+
176+
private
177+
178+
def prettify_comm(comm, dns_server)
179+
if comm.nil?
180+
channel = Rex::Socket::SwitchBoard.best_comm(dns_server)
181+
if channel.nil?
182+
comm_text = nil
183+
else
184+
comm_text = "Session #{channel.sid} (auto)"
185+
end
186+
else
187+
if driver.framework.sessions.include?(comm)
188+
comm_text = "Session #{comm}"
189+
else
190+
comm_text = "Broken session (#{comm})"
191+
end
192+
end
193+
end
194+
195+
def print_dns_set(heading, columns, result_set)
196+
tbl = Table.new(
197+
Table::Style::Default,
198+
'Header' => heading,
199+
'Prefix' => "\n",
200+
'Postfix' => "\n",
201+
'Columns' => columns
202+
)
203+
result_set.each do |row|
204+
tbl << row
205+
end
206+
207+
print(tbl.to_s) if tbl.rows.length > 0
208+
end
209+
210+
def resolver
211+
self.driver.framework.dns_resolver
212+
end
213+
end
214+
215+
end
216+
end
151217
end
218+
end
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
module Rex
2+
module Proto
3+
module DNS
4+
5+
##
6+
# Provides a DNS resolver the ability to use different nameservers
7+
# for different requests, based on the domain being queried.
8+
##
9+
module CustomNameserverProvider
10+
11+
def init
12+
self.entries_with_rules = []
13+
self.entries_without_rules = []
14+
self.next_id = 0
15+
end
16+
17+
# Add a custom nameserver entry to the custom provider
18+
# @param [wildcard_rules] Array<String> The wildcard rules to match a DNS request against
19+
# @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
21+
def add_nameserver(wildcard_rules, dns_server, comm)
22+
entry = {
23+
:wildcard_rules => wildcard_rules,
24+
:dns_server => dns_server,
25+
:comm => comm,
26+
:id => self.next_id
27+
}
28+
self.next_id += 1
29+
if wildcard_rules.empty?
30+
entries_without_rules << entry
31+
else
32+
entries_with_rules << entry
33+
end
34+
end
35+
36+
#
37+
# The custom nameserver entries that have been configured
38+
# @return [Array<Array>] An array containing two elements: The entries with rules, and the entries without rules
39+
def nameserver_entries
40+
[entries_with_rules, entries_without_rules]
41+
end
42+
43+
def purge
44+
init
45+
end
46+
47+
def nameservers_for_packet(packet)
48+
name = packet.question.qName
49+
dns_servers = []
50+
51+
self.entries_with_rules.each do |entry|
52+
entry[:wildcard_rules].each do |rule|
53+
if matches(name, rule)
54+
dns_servers.concat([entry[:dns_server], entry[:comm]])
55+
break
56+
end
57+
end
58+
end
59+
60+
# Only look at the rule-less entries if no rules were found (avoids DNS leaks)
61+
if dns_servers.empty?
62+
self.entries_without_rules.each do |entry|
63+
dns_servers.concat([entry[:dns_server], entry[:comm]])
64+
end
65+
end
66+
dns_servers.uniq!
67+
end
68+
69+
def self.extended(mod)
70+
mod.init
71+
end
72+
73+
private
74+
75+
def matches(domain, pattern)
76+
true
77+
end
78+
79+
attr_accessor :entries_with_rules # Set of custom nameserver entries that specify a rule
80+
attr_accessor :entries_without_rules # Set of custom nameserver entries that do not include a rule
81+
attr_accessor :next_id # The next ID to have been allocated to an entry
82+
end
83+
end
84+
end
85+
end

lib/rex/proto/dns/resolver.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ def proxies=(prox, timeout_added = 250)
110110
end
111111
end
112112

113+
#
114+
# Find the nameservers to use for a given DNS request
115+
# @param dns_message [Dnsruby::Message] The DNS message to be sent
116+
#
117+
# @return [Array<Array>] A list of nameservers, each with Rex::Socket options
118+
def nameservers_for_packet(dns_message)
119+
@config[:nameservers].map {|ns| [ns, {}]}
120+
end
121+
113122
#
114123
# Send DNS request over appropriate transport and process response
115124
#
@@ -119,10 +128,6 @@ def proxies=(prox, timeout_added = 250)
119128
#
120129
# @return [Dnsruby::Message] DNS response
121130
def send(argument, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
122-
if @config[:nameservers].size == 0
123-
raise ResolverError, "No nameservers specified!"
124-
end
125-
126131
method = self.use_tcp? ? :send_tcp : :send_udp
127132

128133
case argument
@@ -136,6 +141,10 @@ def send(argument, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
136141
packet = Rex::Proto::DNS::Packet.encode_drb(net_packet)
137142
end
138143

144+
if nameservers_for_packet(packet).size == 0
145+
raise ResolverError, "No nameservers specified!"
146+
end
147+
139148
# Store packet_data for performance improvements,
140149
# so methods don't keep on calling Packet#encode
141150
packet_data = packet.encode
@@ -195,7 +204,8 @@ def send(argument, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
195204
def send_tcp(packet,packet_data,prox = @config[:proxies])
196205
ans = nil
197206
length = [packet_data.size].pack("n")
198-
@config[:nameservers].each do |ns|
207+
nameservers = nameservers_for_packet(packet)
208+
nameservers.each do |ns, socket_options|
199209
begin
200210
socket = nil
201211
@config[:tcp_timeout].timeout do
@@ -208,6 +218,8 @@ def send_tcp(packet,packet_data,prox = @config[:proxies])
208218
'Context' => @config[:context],
209219
'Comm' => @config[:comm]
210220
}
221+
config.update(socket_options)
222+
211223
if @config[:source_port] > 0
212224
config['LocalPort'] = @config[:source_port]
213225
end
@@ -289,7 +301,8 @@ def send_tcp(packet,packet_data,prox = @config[:proxies])
289301
def send_udp(packet,packet_data)
290302
ans = nil
291303
response = ""
292-
@config[:nameservers].each do |ns|
304+
nameservers = nameservers_for_packet(packet)
305+
nameservers.each do |ns, socket_options|
293306
begin
294307
@config[:udp_timeout].timeout do
295308
begin
@@ -299,6 +312,7 @@ def send_udp(packet,packet_data)
299312
'Context' => @config[:context],
300313
'Comm' => @config[:comm]
301314
}
315+
config.update(socket_options)
302316
if @config[:source_port] > 0
303317
config['LocalPort'] = @config[:source_port]
304318
end

0 commit comments

Comments
 (0)