Skip to content

Commit 38b25f0

Browse files
author
Chris John Riley
committed
Corrected bad coding (sorry)
Added OptEnum and OptPath Checks for nil and empty Added reference Made AlterSerial an advanced option instead of always on
1 parent b75c622 commit 38b25f0

File tree

1 file changed

+44
-25
lines changed

1 file changed

+44
-25
lines changed

modules/auxiliary/scanner/http/impersonate_ssl.rb

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ class Metasploit4 < Msf::Auxiliary
1919
def initialize(info={})
2020
super(update_info(info,
2121
'Name' => 'HTTP SSL Certificate Impersonation',
22-
'Version' => '$Revision$',
2322
'Author' => ' Chris John Riley',
23+
'References' =>
24+
[
25+
['URL', 'http://www.slideshare.net/ChrisJohnRiley/ssl-certificate-impersonation-for-shits-andgiggles']
26+
],
2427
'License' => MSF_LICENSE,
2528
'Description' => %q{
26-
This module request a copy of the remote SSL certificate and creates a local
29+
This module request a copy of the remote SSL certificate and creates a local
2730
(self.signed) version using the information from the remote version. The module
2831
then Outputs (PEM|DER) format private key / certificate and a combined version
2932
for use in Apache or other Metasploit modules requiring SSLCert Inputs for private
@@ -34,46 +37,52 @@ def initialize(info={})
3437
register_options(
3538
[
3639
Opt::RPORT(443),
37-
OptString.new('OUT_FORMAT', [true, "Output format PEM / DER", 'PEM']),
38-
OptString.new('EXPIRATION', [false, "Date the new cert should expire (e.g. 06 May 2012, Yesterday or Now)", '']),
39-
OptString.new('PRIVKEY', [false, "Sign the cert with your own CA private key ;)", '']),
40-
OptString.new('PRIVKEY_PASSWORD', [false, "Password for private key specified in PRIV_KEY (if applicable)", '']),
41-
OptString.new('CA_CERT', [false, "CA Public certificate", '']),
42-
OptString.new('ADD_CN', [false, "Add CN to match spoofed site name (e.g. *.example.com)", '']),
40+
OptEnum.new('OUT_FORMAT', [true, "Output format", 'PEM', ['DER','PEM']]),
41+
OptString.new('EXPIRATION', [false, "Date the new cert should expire (e.g. 06 May 2012, YESTERDAY or NOW)", nil]),
42+
OptPath.new('PRIVKEY', [false, "Sign the cert with your own CA private key", nil]),
43+
OptString.new('PRIVKEY_PASSWORD', [false, "Password for private key specified in PRIV_KEY (if applicable)", nil]),
44+
OptPath.new('CA_CERT', [false, "CA Public certificate", nil]),
45+
OptString.new('ADD_CN', [false, "Add CN to match spoofed site name (e.g. *.example.com)", nil])
46+
], self.class)
47+
48+
register_advanced_options(
49+
[
50+
OptBool.new('AlterSerial', [false, "Alter the serial number slightly to avoif FireFox serial matching", true])
4351
], self.class)
4452
end
4553

4654
def run
4755
print_status("Connecting to #{rhost}:#{rport}")
4856

49-
if (datastore['PRIVKEY'] != '' and datastore['CA_CERT'] != '')
50-
print_status("Signing generated certificate with provided KEY and CA Certificate")
51-
if datastore['PRIVKEY_PASSWORD'] != ''
57+
if not datastore['PRIVKEY'].nil? and not datastore['CA_CERT'].nil?
58+
print_status("Signing generated certificate with provided PRIVATE KEY and CA Certificate")
59+
if not datastore['PRIVKEY_PASSWORD'].nil? and not datastore['PRIVKEY_PASSWORD'].empty?
5260
ca_key = OpenSSL::PKey::RSA.new(File.read(datastore['PRIVKEY']), datastore['PRIVKEY_PASSWORD'])
5361
else
5462
ca_key = OpenSSL::PKey::RSA.new(File.read(datastore['PRIVKEY']))
5563
end
5664
ca = OpenSSL::X509::Certificate.new(File.read(datastore['CA_CERT']))
57-
elsif (datastore['PRIVKEY'] != '' or datastore['CA_CERT'] != '')
65+
elsif not datastore['PRIVKEY'].nil? or not datastore['CA_CERT'].nil?
66+
# error if both PRIVKEY and CA_CERT are not BOTH provided
5867
print_error("CA Certificate AND Private Key must be provided!")
5968
return
6069
end
6170

6271
begin
6372
connect(true, {"SSL" => true}) # Force SSL even for RPORT != 443
64-
cert = OpenSSL::X509::Certificate.new(sock.peer_cert) # Get certificate from remote rhost
73+
cert = OpenSSL::X509::Certificate.new(sock.peer_cert) # Get certificate from remote rhost
6574
disconnect
6675
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
6776
rescue ::Timeout::Error, ::Errno::EPIPE => e
6877
print_error(e.message)
6978
end
7079

71-
if(not cert)
72-
print_error("#{rhost} No certificate subject or CN found")
80+
if not cert
81+
print_error("#{rhost}:#{rport} No certificate subject or CN found")
7382
return
7483
end
7584

76-
print_status("Copying certificate #{cert.subject.to_s} from #{rhost}:#{rport}")
85+
print_status("Copying certificate from #{rhost}:#{rport}\n#{cert.subject.to_s} ")
7786
vprint_status("Original Certifcate Details\n\n#{cert.to_text}")
7887

7988
begin
@@ -89,21 +98,23 @@ def run
8998
end
9099

91100
new_cert = OpenSSL::X509::Certificate.new
92-
ef = OpenSSL::X509::ExtensionFactory.new#(nil,new_cert)
101+
ef = OpenSSL::X509::ExtensionFactory.new
93102

94103
# Duplicate information from the remote certificate
95104
entries = ['version','serial', 'subject', 'not_before','not_after']
96105
entries.each do | ent |
97106
eval("new_cert.#{ent} = cert.#{ent}")
98107
end
99108

100-
if datastore['ADD_CN'] != ''
109+
# add additional Common Name to the new cert
110+
if not datastore['ADD_CN'].nil? and not datastore['ADD_CN'].empty?
101111
new_cert.subject = OpenSSL::X509::Name.new(new_cert.subject.to_a << ["CN", "#{datastore['ADD_CN']}"])
102112
print_status("Adding #{datastore['ADD_CN']} to the end of the certificate subject")
103113
vprint_status("Certificate Subject: #{new_cert.subject}")
104114
end
105115

106-
if datastore['EXPIRATION'] != ''
116+
if not datastore['EXPIRATION'].nil? and not datastore['EXPIRATION'].empty?
117+
# alter the not_after and not_before dates
107118
print_status("Altering certificate expiry information to #{datastore['EXPIRATION']}")
108119

109120
case datastore['EXPIRATION'].downcase
@@ -123,13 +134,21 @@ def run
123134
end
124135

125136
# Alter serial to avoid duplicate issuer/serial detection
126-
if (cert.serial.to_s.length > 1)
127-
new_cert.serial = (cert.serial.to_s[0..-2] + rand(0xFF).to_s).to_i
137+
if datastore['AlterSerial']
138+
if (cert.serial.to_s.length > 1)
139+
# alter last digits of the serial number
140+
new_cert.serial = (cert.serial.to_s[0..-2] + rand(0xFF).to_s).to_i
141+
else
142+
# serial is too small, create random serial
143+
vprint_error("The serial number of the original cert is too short. Creating new random serial")
144+
new_cert.serial = rand(0xFFFF)
145+
end
128146
else
129-
new_cert.serial = rand(0xFFFF)
147+
# match serial number
148+
new_cert.serial = cert.serial.to_s
130149
end
131150

132-
if datastore['PRIVKEY'] != ''
151+
if not datastore['PRIVKEY'].nil? and not datastore['PRIVKEY'].empty?
133152
new_cert.public_key = ca_key.public_key
134153
ef.subject_certificate = ca
135154
ef.issuer_certificate = ca
@@ -140,7 +159,7 @@ def run
140159
new_cert.public_key = new_key.public_key
141160
ef.subject_certificate = new_cert
142161
ef.issuer_certificate = new_cert
143-
if datastore['ADD_CN'] != ''
162+
if not datastore['ADD_CN'].nil? and not datastore['ADD_CN'].empty?
144163
new_cert.issuer = new_cert.subject
145164
else
146165
new_cert.issuer = cert.subject
@@ -152,7 +171,7 @@ def run
152171
ef.create_extension("subjectKeyIdentifier","hash"),
153172
]
154173

155-
if datastore['PRIVKEY'] != ''
174+
if not datastore['PRIVKEY'].nil? and not datastore['PRIVKEY'].empty?
156175
new_cert.sign(ca_key, eval("OpenSSL::Digest::#{hashtype.upcase}.new"))
157176
new_key = ca_key # Set for file output
158177
else

0 commit comments

Comments
 (0)