af7eef231c
First, we need to handle public keys with strength not measured on the same bit scale as RSA keys. This fixes handshakes for ECDSA and others. Second, depending on the host we are talking to, we may not have a peer cert. Handle this properly by checking first on the socket before using it.
144 lines
4.2 KiB
Ruby
144 lines
4.2 KiB
Ruby
##
|
|
# This module requires Metasploit: http://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
class MetasploitModule < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Remote::Tcp
|
|
include Msf::Auxiliary::WmapScanSSL
|
|
include Msf::Auxiliary::Scanner
|
|
include Msf::Auxiliary::Report
|
|
|
|
include Rex::Socket::Comm
|
|
|
|
def initialize
|
|
super(
|
|
'Name' => 'HTTP SSL Certificate Information',
|
|
'Description' => 'Parse the server SSL certificate to obtain the common name and signature algorithm',
|
|
'Author' =>
|
|
[
|
|
'et', #original module
|
|
'Chris John Riley', #additions
|
|
'Veit Hailperin <hailperv[at]gmail.com>', # checks for public key size, valid time
|
|
],
|
|
'License' => MSF_LICENSE
|
|
)
|
|
register_options([
|
|
Opt::RPORT(443)
|
|
], self.class)
|
|
end
|
|
|
|
# Fingerprint a single host
|
|
def run_host(ip)
|
|
|
|
begin
|
|
|
|
connect(true, {"SSL" => true}) #Force SSL
|
|
|
|
if sock.respond_to? :peer_cert
|
|
cert = OpenSSL::X509::Certificate.new(sock.peer_cert)
|
|
end
|
|
|
|
disconnect
|
|
|
|
if cert
|
|
print_status("Subject: #{cert.subject}")
|
|
print_status("Issuer: #{cert.issuer}")
|
|
print_status("Signature Alg: #{cert.signature_algorithm}")
|
|
|
|
# If we use ECDSA rather than RSA, our metrics for key size are different
|
|
public_key_size = 0
|
|
if cert.public_key.respond_to? :n
|
|
public_key_size = cert.public_key.n.num_bytes * 8
|
|
print_status("Public Key Size: #{public_key_size} bits")
|
|
end
|
|
print_status("Not Valid Before: #{cert.not_before}")
|
|
print_status("Not Valid After: #{cert.not_after}")
|
|
|
|
# Checks for common properties of self signed certificates
|
|
caissuer = (/CA Issuers - URI:(.*?),/i).match(cert.extensions.to_s)
|
|
|
|
if caissuer.to_s.empty?
|
|
print_good("Certificate contains no CA Issuers extension... possible self signed certificate")
|
|
else
|
|
print_status(caissuer.to_s[0..-2])
|
|
end
|
|
|
|
if cert.issuer.to_s == cert.subject.to_s
|
|
print_good("Certificate Subject and Issuer match... possible self signed certificate")
|
|
end
|
|
|
|
alg = cert.signature_algorithm
|
|
|
|
if alg.downcase.include? "md5"
|
|
print_status("WARNING: Signature algorithm using MD5 (#{alg})")
|
|
end
|
|
|
|
vhostn = nil
|
|
cert.subject.to_a.each do |n|
|
|
vhostn = n[1] if n[0] == 'CN'
|
|
end
|
|
if public_key_size > 0
|
|
if public_key_size == 1024
|
|
print_status("WARNING: Public Key only 1024 bits")
|
|
elsif public_key_size < 1024
|
|
print_status("WARNING: Weak Public Key: #{public_key_size} bits")
|
|
end
|
|
end
|
|
if cert.not_after < Time.now
|
|
print_status("WARNING: Certificate not valid anymore")
|
|
end
|
|
if cert.not_before > Time.now
|
|
print_status("WARNING: Certificate not valid yet")
|
|
end
|
|
|
|
if vhostn
|
|
print_status("Has common name #{vhostn}")
|
|
|
|
# Store the virtual hostname for HTTP
|
|
report_note(
|
|
:host => ip,
|
|
:port => rport,
|
|
:proto => 'tcp',
|
|
:type => 'http.vhost',
|
|
:data => {:name => vhostn}
|
|
)
|
|
|
|
# Store the SSL certificate itself
|
|
report_note(
|
|
:host => ip,
|
|
:proto => 'tcp',
|
|
:port => rport,
|
|
:type => 'ssl.certificate',
|
|
:data => {
|
|
:cn => vhostn,
|
|
:subject => cert.subject.to_a,
|
|
:algorithm => alg,
|
|
:valid_from => cert.not_before,
|
|
:valid_after => cert.not_after,
|
|
:key_size => public_key_size
|
|
|
|
}
|
|
)
|
|
|
|
# Update the server hostname if necessary
|
|
if vhostn !~ /localhost|snakeoil/i
|
|
report_host(
|
|
:host => ip,
|
|
:name => vhostn
|
|
)
|
|
end
|
|
|
|
end
|
|
else
|
|
print_status("No certificate subject or common name found")
|
|
end
|
|
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
|
rescue ::Timeout::Error, ::Errno::EPIPE
|
|
end
|
|
end
|
|
end
|