Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/spoof/dns/compare_results.rb
19567 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'net/dns'
7
require 'resolv'
8
9
class MetasploitModule < Msf::Auxiliary
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'DNS Lookup Result Comparison',
16
'Description' => %q{
17
This module can be used to determine differences
18
in the cache entries between two DNS servers. This is
19
primarily useful for detecting cache poisoning attacks,
20
but can also be used to detect geo-location load balancing.
21
},
22
'Author' => [ 'hdm' ],
23
'License' => MSF_LICENSE,
24
'References' => [
25
],
26
'DisclosureDate' => '2008-07-21',
27
'Notes' => {
28
'Stability' => [CRASH_SAFE],
29
'SideEffects' => [],
30
'Reliability' => []
31
}
32
)
33
)
34
35
register_options(
36
[
37
OptAddress.new('BASEDNS', [ true, 'The DNS cache server to use as a baseline', '4.2.2.3' ]),
38
OptAddress.new('TARGDNS', [ true, 'The DNS cache server to test', nil ]),
39
OptString.new('NAMES', [ true, 'The list of host names that should be tested (comma separated)', 'www.google.com,www.yahoo.com,www.msn.com']),
40
OptBool.new('CHECK_AUTHORITY', [ false, 'Set this to true to verify authority records', false ]),
41
OptBool.new('CHECK_ADDITIONAL', [ false, 'Set this to true to verify additional records', false ]),
42
43
]
44
)
45
end
46
47
def run
48
base_addr = datastore['BASEDNS']
49
targ_addr = datastore['TARGDNS']
50
check_ar = datastore['CHECK_ADDITIONAL']
51
check_aa = datastore['CHECK_AUTHORITY']
52
names = datastore['NAMES'].split(',').map(&:strip)
53
recurse = true
54
results = {}
55
56
print_status("Comparing results between #{base_addr} and #{targ_addr}...")
57
58
base_sock = Rex::Socket.create_udp(
59
'PeerHost' => base_addr,
60
'PeerPort' => 53
61
)
62
63
targ_sock = Rex::Socket.create_udp(
64
'PeerHost' => targ_addr,
65
'PeerPort' => 53
66
)
67
68
names.each do |entry|
69
entry.strip!
70
next if entry.empty?
71
72
req = Resolv::DNS::Message.new
73
req.add_question(entry, Resolv::DNS::Resource::IN::A)
74
req.rd = recurse ? 1 : 0
75
76
buf = req.encode
77
print_status("Querying servers for #{entry}...")
78
base_sock.put(buf)
79
targ_sock.put(buf)
80
81
base_res, = base_sock.recvfrom(65535, 3.0)
82
targ_res, = targ_sock.recvfrom(65535, 3.0)
83
84
if !(base_res && targ_res && !base_res.empty? && !targ_res.empty?)
85
print_error(' Error: The baseline server did not respond to our request.') if !(base_res && !base_res.empty?)
86
print_error(' Error: The target server did not respond to our request.') if !(targ_res && !targ_res.empty?)
87
next
88
end
89
90
base_res = Resolv::DNS::Message.decode(base_res)
91
targ_res = Resolv::DNS::Message.decode(targ_res)
92
93
[base_res, targ_res].each do |res|
94
hkey = (res == base_res) ? :base : :targ
95
96
rrset = res.answer
97
rrset += res.authority if check_aa
98
rrset += res.additional if check_ar
99
100
rrset.each do |ref|
101
name, _, data = ref
102
103
name.to_s
104
anst = data.class.to_s.gsub(/^.*Resolv::DNS::Resource::IN::/, '')
105
case data
106
when Resolv::DNS::Resource::IN::NS
107
data = data.name.to_s
108
when Resolv::DNS::Resource::IN::MX
109
data = data.exchange.to_s
110
when Resolv::DNS::Resource::IN::A
111
data = data.address.to_s
112
when Resolv::DNS::Resource::IN::TXT
113
data = data.strings.join
114
when Resolv::DNS::Resource::IN::CNAME
115
data = data.name.to_s
116
else
117
data = anst
118
end
119
120
results[entry] ||= {}
121
results[entry][hkey] ||= {}
122
results[entry][hkey][anst] ||= []
123
results[entry][hkey][anst] << data
124
end
125
end
126
end
127
128
[ base_sock, targ_sock ].each(&:close)
129
130
print_status("Analyzing results for #{results.keys.length} entries...")
131
132
results.each_key do |entry|
133
n_add = []
134
n_sub = []
135
136
# Look for additional entries in the target NS
137
if (results[entry][:targ])
138
results[entry][:targ].each_key do |rtype|
139
next unless !(results[entry][:base]) || !(results[entry][:base][rtype])
140
141
results[entry][:targ][rtype].sort.each do |ref|
142
n_sub << (" + #{entry} #{rtype} #{ref}")
143
end
144
end
145
end
146
147
if (results[entry][:base])
148
results[entry][:base].each_key do |rtype|
149
# Look for missing entries in the target NS
150
if !(results[entry][:targ]) || !(results[entry][:targ][rtype])
151
results[entry][:base][rtype].sort.each do |ref|
152
n_sub << (" - #{entry} #{rtype} #{ref}")
153
end
154
next
155
end
156
157
# Look for differences
158
next unless (results[entry][:base][rtype].sort != results[entry][:targ][rtype].sort)
159
160
results[entry][:base][rtype].sort.each do |ref|
161
if !results[entry][:targ][rtype].include?(ref)
162
n_sub << (" - #{entry} #{rtype} #{ref}")
163
end
164
end
165
results[entry][:targ][rtype].sort.each do |ref|
166
if !results[entry][:base][rtype].include?(ref)
167
n_add << (" + #{entry} #{rtype} #{ref}")
168
end
169
end
170
end
171
end
172
173
n_sub.each { |s| print_status(s) }
174
n_add.each { |s| print_status(s) }
175
end
176
end
177
end
178
179