Nmap Development mailing list archives

Re: Request: public key retrieval in nmap.get_ssl_certificate


From: Brandon Enright <bmenrigh () ucsd edu>
Date: Thu, 28 Jan 2010 21:56:06 +0000

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Thu, 28 Jan 2010 12:36:03 -0700
David Fifield <david () bamsoftware com> wrote:

All,

I got a request for ssl-cert.nse to return the length of the
certificate's public key, so the scritp can be used to check for short
keys.

Who wants to add support for this? I think it will be straightforward
to add a new accessor method in nse_ssl_cert.cc. I see the addition
of a new function, ssl_cert_pubkey, that calls the function
X509_get_pubkey and returns a table representing the key information.

David Fifield

I wrote a perl script that pulls the cert out of the script output and
passes it through openssl like so:


                    # $ cat | openssl x509 -text -fingerprint -noout            
                    my @cmd = ('openssl',
                               'x509', '-text', '-fingerprint', '-noout');

                    my ($ccode, $in, $out, $err, $rcode);
                    $in = $cert;
                    $ccode = run \@cmd, \$in, \$out, \$err, timeout(5);
                    $rcode = $?;

                    unless ((defined $ccode) && ($rcode == 0)) {
                        warn $err if ((defined $err) && ($err ne ''));
                    }
                    else {
                        $cert_text = $out;
                        #warn 'got ', $out, "\n";                               
                    }


While we're on the topic of SSL certs and checking for bad things, here
are the things I'm checking for in snippets of hopefully-readable
perl.  I know some of this is out of context but when you see a "push
@warning" that is a message that will be reported by the script.

                # The sig alg                                                   
                if ($cert_text =~ m/Signature Algorithm: (\S+)/) {
                    my $sig_alg = $1;

                    if ($sig_alg =~ m/md5/i) {
                        push @warning, {('name'=>'SSLCERT_MD5',
                                         'severity'=>4,
                                         'text'=>'SSL Cert on '
                                         . $ps_port . ' is signed with MD5')};
                    }
                    elsif ($sig_alg =~ m/md2/i) {
                        push @warning, {('name'=>'SSLCERT_MD2',
                                         'severity'=>7,
                                         'text'=>'SSL Cert on '
                                         . $ps_port . ' is signed with MD2')};
                    }
                    elsif ($sig_alg =~ m/md4/i) {
                        push @warning, {('name'=>'SSLCERT_MD4',
                                         'severity'=>6,
                                         'text'=>'SSL Cert on '
                                         . $ps_port . ' is signed with MD4')};
                    }
                    elsif ($sig_alg !~ m/sha[1235]/i) {
                        push @warning, {('name'=>'SSLCERT_NOTSHA',
                                         'severity'=>5,
                                         'text'=>'SSL Cert on '
                                         . $ps_port . ' is signed with a '
                                         . 'SHA family algorithm')};
                    }
                }


                # The RSA size                                                  
                if ($cert_text =~ m/Modulus \((\d+) bit\):/) {
                    my $size = $1;

                    if ($size < 1024) {
                        push @warning, {('name'=>'SSLCERT_SMALL',
                                         'severity'=>6,
                                         'text'=>'SSL Cert on '
                                         . $ps_port . ' uses a small key ('
                                         . $size . ')')};
                    }

                    if (($size != 512) &&
                        ($size != 1024) &&
                        ($size != 1536) &&
                        ($size != 2048) &&
                        ($size != 3072) &&
                        ($size != 4096)) {
                        push @warning, {('name'=>'SSLCERT_ODDSIZE',
                                         'severity'=>2,
                                         'text'=>'SSL Cert on '
                                         . $ps_port . ' uses odd sized key ('
                                         . $size . ')')};
                    }
                }


                # The issuer and subject                                        
                if ($cert_text =~ m/Issuer: ([^\r\n]+)/) {
                    my $issuer = $1;
                    if ($cert_text =~ m/Subject: ([^\r\n]+)/) {
                        my $subject = $1;
                        my $selfsigned = 0;

                        if ($issuer eq $subject) {
                            push @warning, {('name'=>'SSLCERT_SELFSIG',
                                             'severity'=>4,
                                             'text'=>'SSL Cert on '
                                             . $ps_port . ' is self-signed')};

                            $selfsigned = 1;
                        }

                        if ($issuer =~ m/Globus\sSimple|DigiCert|Thawt|         
                                         Equifax|cacert|Go\sDaddy|              
                                         IPS\sCertification|Comodo\sCA|         
                                         VeriSign\sTrust|Entrust\.net           
                                        /ix) {
                            push @warning, {('name'=>'SSLCERT_CASIG',
                                             'severity'=>1,
                                             'text'=>'SSL Cert on '
                                             . $ps_port . ' is not '
                                             . 'self-signed and may hold '
                                             . 'more trust')};
                        }
                        else {
                            if ($issuer ne $subject) {
                                # To try to find more CAs                       
                                #warn 'Issuer: ', $issuer, "\n";                
                            }
                        }

                        if ($subject =~ m/CN=(.*?)(?:, |\/|$)/) {
                            my $cn = $1;
                            unless ($cn =~ m/^[a-zA-Z0-9@._*-]+$/) {
                                push @warning, {('name'=>'SSLCERT_BADCN',
                                                 'severity'=>4,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' does not have '
                                                 . 'a well-formed CN')};
                            }

                            if ($cn =~ m/^\*/) {
                                push @warning, {('name'=>'SSLCERT_WILDCARD',
                                                 'severity'=>4,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' has wildcard'
                                                 . ' CN (' . $cn . ')')};
                            }

                            # Look for evil or fake CNs                         
                            if ($selfsigned == 1) {
                                if ($cn =~ m/Microsoft/i) {
                                    push @warning, {('name'=>'SSLCERT_FAKECN',
                                                     'severity'=>8,
                                                     'text'=>'SSL Cert on '
                                                 . $ps_port . ' appears to'
                                        . ' be faking a company CN')};
                                }

                                if ($cn =~ m/\b(?:evil|                         
                                                hack(?:ed)?|                    
                                                [op]wn(?:ed)?                   
                                               )\b/xi) {
                                    push @warning, {('name'=>'SSLCERT_EVILCN',
                                                     'severity'=>8,
                                                     'text'=>'SSL Cert on '
                                                 . $ps_port . ' appears to '
                                        . ' be indicate a malicious service')};
                                }
                            }

                        }
                        else {
                            push @warning, {('name'=>'SSLCERT_NOCN',
                                             'severity'=>4,
                                             'text'=>'SSL Cert on '
                                             . $ps_port . ' does not have '
                                             . 'a CN in the subject')};

                            #warn 'Subject: ', $subject, "\n";                  
                        }

                    }
                }

                # The validity dates                                            
                if ($cert_text =~ m/Not Before: ([^\r\n]+(\d{4}) GMT)/) {
                    my $sbadyear = 0;
                    my $starttime = 0;

                    if (($2 < 1970) || ($2 >= 2038)) {
                        $sbadyear = 1;
                    }
                    else {
                        #warn 'start: ', $1, "\n";                              
                        $starttime = str2time($1);

                        unless (defined $starttime) {
                            $sbadyear = 1;
                        }
                    }

                    if ($cert_text =~ m/Not After : ([^\r\n]+(\d{4}) GMT)/) {
                        my $ebadyear = 0;
                        my $endtime = 0;

                        if (($2 < 1970) || ($2 >= 2038)) {
                            $ebadyear = 1;
                        }
                        else {
                            #warn 'end: ', $1, "\n";                            
                            $endtime = str2time($1);

                            unless (defined $endtime) {
                                $ebadyear = 1;
                            }
                        }

                        if (($sbadyear == 1) || ($ebadyear == 1)) {
                            push @warning, {('name'=>'SSLCERT_BADYEAR',
                                             'severity'=>2,
                                             'text'=>'SSL Cert on '
                                             . $ps_port . ' is valid for a '
                                             . 'nonsensical date range')};
                        }
                        else {
                            if ($starttime >= $endtime) {
                                push @warning, {('name'=>'SSLCERT_NEVER',
                                                 'severity'=>2,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' is never valid'
                                                 . ' based on date range')};
                            }
                            if ($starttime > $cur_time) {
                                push @warning, {('name'=>'SSLCERT_FUTURE',
                        L                        'severity'=>2,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' is only valid '
                                                 . 'in the future')};
                            }
                            if ($endtime < $cur_time) {
                                push @warning, {('name'=>'SSLCERT_EXPIRED',
                                                 'severity'=>3,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' has expired')};
                            }
                            if (($endtime > $cur_time) &&
                                ($endtime < $cur_time + 60 * 24 * 60 * 60)) {
                                push @warning, {('name'=>'SSLCERT_EXPIRESOON',
                                                 'severity'=>1,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' will expire'
                                                 . ' soon')};
                            }
                            if (($endtime - $starttime) >
                                (10 * 356.4 * 24 * 60 * 60)) {
                                push @warning, {('name'=>'SSLCERT_DECADE',
                                                 'severity'=>3,
                                                 'text'=>'SSL Cert on '
                                                 . $ps_port . ' is valid for '
                                                 . 'more than a decade')};
                            }
                        } # End is good year in dates                           
                    }



Hopefully other people wanting to audit their organization's SSL
deployments will find this useful.

Brandon

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.11 (GNU/Linux)

iEYEARECAAYFAktiCAUACgkQqaGPzAsl94IqtwCeM+l660HJLUQ0/mMKuzuPj88n
l/4AoItH7/t6PWjAnucKsNCmLysXX9ha
=/kA4
-----END PGP SIGNATURE-----
_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/


Current thread: