############################################################################### # SPDX-License-Identifier: GPL-3.0-only # PURPOSE: Test certificate information ############################################################################### sub nikto_ssl_init { my $id = { name => "ssl", full_name => "SSL and cert checks", author => "Sullo", description => "Perform checks on SSL/Certificates", hooks => { scan => { method => \&nikto_ssl, } }, copyright => "2010 Chris Sullo" }; return $id; } sub nikto_ssl { my ($mark) = @_; if ($mark->{ssl}) { my @cn_names; my @san_names; my $match = 0; # Extract CN from subject if ($mark->{'ssl_cert_subject'} =~ /CN=([^$ \/]+)/) { push(@cn_names, $1); } # Extract SAN names if ($mark->{'ssl_cert_altnames'} ne '') { foreach my $n (split(/, /, $mark->{'ssl_cert_altnames'})) { push(@san_names, $n); } } # Combine all names for validation my @all_names = (@cn_names, @san_names); @all_names = unique_vals(@all_names); # Create detailed name lists for error messages my $cn_list = @cn_names ? join(", ", @cn_names) : "none"; my $san_list = @san_names ? join(", ", @san_names) : "none"; my $allnames = join(", ", @all_names); foreach my $cert_name (@all_names) { next unless $cert_name; # Skip empty names # straight up match if (lc($mark->{'hostname'}) eq lc($cert_name)) { $match = 1; } # wildcard cert elsif ($cert_name =~ /^\*/) { add_vulnerability($mark, "/: Server is using a wildcard certificate: $cert_name", 999992, "https://en.wikipedia.org/wiki/Wildcard_certificate"); $cert_name =~ s/^\*\.//; $cert_name = rquote($cert_name); # must match leading dot # only one level of subdomain allowed if ($mark->{'hostname'} =~ /^(.*)\.?$cert_name/i) { my $matched = $1; my $tldcount = ($matched =~ tr/\.//); if ($tldcount <= 1) { $match = 1; } } } last if $match; } if (!$match) { my $error_msg = "/: Hostname '$mark->{'hostname'}' does not match certificate names"; $error_msg .= " (CN: $cn_list, SAN: $san_list)"; add_vulnerability($mark, $error_msg, 999993, "https://cwe.mitre.org/data/definitions/297.html"); } } } sub unique_vals { my %seen; grep !$seen{$_}++, @_; } 1;