Merge branch 'impersonate_ssl_tweak' of git://github.com/ChrisJohnRiley/metasploit-framework into ChrisJohnRiley-impersonate_ssl_tweak
commit
ee7e502e89
|
@ -19,11 +19,14 @@ class Metasploit4 < Msf::Auxiliary
|
|||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'HTTP SSL Certificate Impersonation',
|
||||
'Version' => '$Revision$',
|
||||
'Author' => ' Chris John Riley',
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://www.slideshare.net/ChrisJohnRiley/ssl-certificate-impersonation-for-shits-andgiggles']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Description' => %q{
|
||||
This module request a copy of the remote SSL certificate and creates a local
|
||||
This module request a copy of the remote SSL certificate and creates a local
|
||||
(self.signed) version using the information from the remote version. The module
|
||||
then Outputs (PEM|DER) format private key / certificate and a combined version
|
||||
for use in Apache or other Metasploit modules requiring SSLCert Inputs for private
|
||||
|
@ -34,46 +37,52 @@ class Metasploit4 < Msf::Auxiliary
|
|||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptString.new('OUT_FORMAT', [true, "Output format PEM / DER", 'PEM']),
|
||||
OptString.new('EXPIRATION', [false, "Date the new cert should expire (e.g. 06 May 2012, Yesterday or Now)", '']),
|
||||
OptString.new('PRIVKEY', [false, "Sign the cert with your own CA private key ;)", '']),
|
||||
OptString.new('PRIVKEY_PASSWORD', [false, "Password for private key specified in PRIV_KEY (if applicable)", '']),
|
||||
OptString.new('CA_CERT', [false, "CA Public certificate", '']),
|
||||
OptString.new('ADD_CN', [false, "Add CN to match spoofed site name (e.g. *.example.com)", '']),
|
||||
OptEnum.new('OUT_FORMAT', [true, "Output format", 'PEM', ['DER','PEM']]),
|
||||
OptString.new('EXPIRATION', [false, "Date the new cert should expire (e.g. 06 May 2012, YESTERDAY or NOW)", nil]),
|
||||
OptPath.new('PRIVKEY', [false, "Sign the cert with your own CA private key", nil]),
|
||||
OptString.new('PRIVKEY_PASSWORD', [false, "Password for private key specified in PRIV_KEY (if applicable)", nil]),
|
||||
OptPath.new('CA_CERT', [false, "CA Public certificate", nil]),
|
||||
OptString.new('ADD_CN', [false, "Add CN to match spoofed site name (e.g. *.example.com)", nil])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('AlterSerial', [false, "Alter the serial number slightly to avoif FireFox serial matching", true])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
print_status("Connecting to #{rhost}:#{rport}")
|
||||
|
||||
if (datastore['PRIVKEY'] != '' and datastore['CA_CERT'] != '')
|
||||
print_status("Signing generated certificate with provided KEY and CA Certificate")
|
||||
if datastore['PRIVKEY_PASSWORD'] != ''
|
||||
if not datastore['PRIVKEY'].nil? and not datastore['CA_CERT'].nil?
|
||||
print_status("Signing generated certificate with provided PRIVATE KEY and CA Certificate")
|
||||
if not datastore['PRIVKEY_PASSWORD'].nil? and not datastore['PRIVKEY_PASSWORD'].empty?
|
||||
ca_key = OpenSSL::PKey::RSA.new(File.read(datastore['PRIVKEY']), datastore['PRIVKEY_PASSWORD'])
|
||||
else
|
||||
ca_key = OpenSSL::PKey::RSA.new(File.read(datastore['PRIVKEY']))
|
||||
end
|
||||
ca = OpenSSL::X509::Certificate.new(File.read(datastore['CA_CERT']))
|
||||
elsif (datastore['PRIVKEY'] != '' or datastore['CA_CERT'] != '')
|
||||
elsif not datastore['PRIVKEY'].nil? or not datastore['CA_CERT'].nil?
|
||||
# error if both PRIVKEY and CA_CERT are not BOTH provided
|
||||
print_error("CA Certificate AND Private Key must be provided!")
|
||||
return
|
||||
end
|
||||
|
||||
begin
|
||||
connect(true, {"SSL" => true}) # Force SSL even for RPORT != 443
|
||||
cert = OpenSSL::X509::Certificate.new(sock.peer_cert) # Get certificate from remote rhost
|
||||
cert = OpenSSL::X509::Certificate.new(sock.peer_cert) # Get certificate from remote rhost
|
||||
disconnect
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
|
||||
rescue ::Timeout::Error, ::Errno::EPIPE => e
|
||||
print_error(e.message)
|
||||
end
|
||||
|
||||
if(not cert)
|
||||
print_error("#{rhost} No certificate subject or CN found")
|
||||
if not cert
|
||||
print_error("#{rhost}:#{rport} No certificate subject or CN found")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Copying certificate #{cert.subject.to_s} from #{rhost}:#{rport}")
|
||||
print_status("Copying certificate from #{rhost}:#{rport}\n#{cert.subject.to_s} ")
|
||||
vprint_status("Original Certifcate Details\n\n#{cert.to_text}")
|
||||
|
||||
begin
|
||||
|
@ -89,7 +98,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
new_cert = OpenSSL::X509::Certificate.new
|
||||
ef = OpenSSL::X509::ExtensionFactory.new#(nil,new_cert)
|
||||
ef = OpenSSL::X509::ExtensionFactory.new
|
||||
|
||||
# Duplicate information from the remote certificate
|
||||
entries = ['version','serial', 'subject', 'not_before','not_after']
|
||||
|
@ -97,13 +106,15 @@ class Metasploit4 < Msf::Auxiliary
|
|||
eval("new_cert.#{ent} = cert.#{ent}")
|
||||
end
|
||||
|
||||
if datastore['ADD_CN'] != ''
|
||||
# add additional Common Name to the new cert
|
||||
if not datastore['ADD_CN'].nil? and not datastore['ADD_CN'].empty?
|
||||
new_cert.subject = OpenSSL::X509::Name.new(new_cert.subject.to_a << ["CN", "#{datastore['ADD_CN']}"])
|
||||
print_status("Adding #{datastore['ADD_CN']} to the end of the certificate subject")
|
||||
vprint_status("Certificate Subject: #{new_cert.subject}")
|
||||
end
|
||||
|
||||
if datastore['EXPIRATION'] != ''
|
||||
if not datastore['EXPIRATION'].nil? and not datastore['EXPIRATION'].empty?
|
||||
# alter the not_after and not_before dates
|
||||
print_status("Altering certificate expiry information to #{datastore['EXPIRATION']}")
|
||||
|
||||
case datastore['EXPIRATION'].downcase
|
||||
|
@ -123,13 +134,21 @@ class Metasploit4 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
# Alter serial to avoid duplicate issuer/serial detection
|
||||
if (cert.serial.to_s.length > 1)
|
||||
new_cert.serial = (cert.serial.to_s[0..-2] + rand(0xFF).to_s).to_i
|
||||
if datastore['AlterSerial']
|
||||
if (cert.serial.to_s.length > 1)
|
||||
# alter last digits of the serial number
|
||||
new_cert.serial = (cert.serial.to_s[0..-2] + rand(0xFF).to_s).to_i
|
||||
else
|
||||
# serial is too small, create random serial
|
||||
vprint_error("The serial number of the original cert is too short. Creating new random serial")
|
||||
new_cert.serial = rand(0xFFFF)
|
||||
end
|
||||
else
|
||||
new_cert.serial = rand(0xFFFF)
|
||||
# match serial number
|
||||
new_cert.serial = cert.serial.to_s
|
||||
end
|
||||
|
||||
if datastore['PRIVKEY'] != ''
|
||||
if not datastore['PRIVKEY'].nil? and not datastore['PRIVKEY'].empty?
|
||||
new_cert.public_key = ca_key.public_key
|
||||
ef.subject_certificate = ca
|
||||
ef.issuer_certificate = ca
|
||||
|
@ -140,7 +159,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
new_cert.public_key = new_key.public_key
|
||||
ef.subject_certificate = new_cert
|
||||
ef.issuer_certificate = new_cert
|
||||
if datastore['ADD_CN'] != ''
|
||||
if not datastore['ADD_CN'].nil? and not datastore['ADD_CN'].empty?
|
||||
new_cert.issuer = new_cert.subject
|
||||
else
|
||||
new_cert.issuer = cert.subject
|
||||
|
@ -152,7 +171,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
ef.create_extension("subjectKeyIdentifier","hash"),
|
||||
]
|
||||
|
||||
if datastore['PRIVKEY'] != ''
|
||||
if not datastore['PRIVKEY'].nil? and not datastore['PRIVKEY'].empty?
|
||||
new_cert.sign(ca_key, eval("OpenSSL::Digest::#{hashtype.upcase}.new"))
|
||||
new_key = ca_key # Set for file output
|
||||
else
|
||||
|
|
Loading…
Reference in New Issue