Skip to content

Commit ee7e502

Browse files
committed
Merge branch 'impersonate_ssl_tweak' of git://github.com/ChrisJohnRiley/metasploit-framework into ChrisJohnRiley-impersonate_ssl_tweak
2 parents cd257f6 + 38b25f0 commit ee7e502

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)