SSL Certificate 'commonName' Mismatch

2010-04-03T00:00:00
ID SSL_CERT_CN_MISMATCH.NASL
Type nessus
Reporter Tenable
Modified 2017-06-05T00:00:00

Description

The service running on the remote host presents an SSL certificate for which the 'commonName' (CN) attribute does not match the hostname on which the service listens.

                                        
                                            #TRUSTED 999271061b26ba14053e9f828f35e57494d00ccf53eeb7de21503d4916d97fa00386f8c28d589800b87f1f70fe360478f1970c1b9430e4474cc21400d7b876f55759b6d8e02172bc4bcd53619a25617557e0e4acef38db0641cbf059d1edcd76b49b4e5a521a5acf81ab1240bcfbc3bdc1dd2927dfc6634e16a77a95195e327f9b785c423e0545abe6febecb823c4e1c8673f77efd5521d4a035060f23c6785cee38d7d6ead374b4ecc2b48e507c25553ac323b28827fa8a590fd6a40e6fd4c662bdc502caacb6510561930740878baa76249672b17792a5ab1cacffa7c5afa3218d6484b5df2e78130cf6e9d5885c841f17c95fce3240cdf86292f16330dc031dfce25913df2e99ecb4bf47bd06626c76341bdbfdf72d91a02395448d178a85dbebdc0dc517302e1111c5567b6bf96a28254ddbeb25bce54d46c2127f4e656fe9d03ffbcd4b9d1c78964c2df66e2521268aac6f7ef45de63dc39ac7e4b15f8953dfc3642c6efb4c4089e182c9b30519e97fbf19465ae42facd56f55ab4471b3b70f054f1e9958c3507380e6f99738bd2d877619958cf00fc90d5b79b2afab3b9fe47938a15b6daf8d83112bb60fd5d5a9d56f84f67672e6f1eff09c7e6da99f40dc827b848e2843194373273ae14c36543ab894c8487c4aff62dbe396a3aa19a46adf173facd87dce4aa72d82e1e05c119588fb471140683cf3e789c5c2bec5
#
# (C) Tenable Network Security, Inc.
#

if ( NASL_LEVEL < 3208 ) exit(0);

include("compat.inc");

if (description)
{
 script_id(45410);
 script_version("1.20");
 script_set_attribute(attribute:"plugin_modification_date", value:"2017/06/05");

 script_name(english:"SSL Certificate 'commonName' Mismatch");
 script_summary(english:"Compare the X509 CN with the hostname.");

 script_set_attribute(attribute:"synopsis", value:
"The 'commonName' (CN) attribute in the SSL certificate does not match
the hostname.");
 script_set_attribute(attribute:"description", value:
"The service running on the remote host presents an SSL certificate for
which the 'commonName' (CN) attribute does not match the hostname on
which the service listens.");
 script_set_attribute(attribute:"solution", value:
"If the machine has several names, make sure that users connect to the
service through the DNS hostname that matches the common name in the
certificate.");
 script_set_attribute(attribute:"risk_factor", value:"None");
 script_set_attribute(attribute:"plugin_publication_date", value:"2010/04/03");
 script_set_attribute(attribute:"plugin_type", value:"remote");
 script_end_attributes();

 script_category(ACT_GATHER_INFO);
 script_copyright(english:"This script is Copyright (C) 2010-2017 Tenable Network Security, Inc.");
 script_family(english:"General");

 script_dependencies("ssl_supported_versions.nasl");
 script_require_keys("SSL/Supported");

 exit(0);
}

include("global_settings.inc");
include("resolv_func.inc");
include("x509_func.inc");

get_kb_item_or_exit("SSL/Supported");

# Compile a list of names for this host from all name services.
host_names = make_list();

addr = get_host_ip();

# NetBIOS Name Service.
name = get_kb_item("SMB/name");
if (name && name != addr)
{
  # Add the short name.
  host_names = make_list(host_names, tolower(name));

  domain = get_kb_item("SMB/domain");
  if (domain)
  {
    name += "." + domain;

    # Add the full name.
    host_names = make_list(host_names, tolower(name));
  }
}

# Domain Name Service.
name = get_host_name();
if (name != addr)
  host_names = make_list(host_names, tolower(name));

host_names = list_uniq(host_names);

# Get a port that uses SSL or StartTLS.
port = get_ssl_ports(fork:TRUE);
if (isnull(port))
  exit(1, "The host does not appear to have any SSL-based services.");

# Get the server's certificate.
cert = get_server_cert(port:port, encoding:"der");
if (isnull(cert))
  exit(1, "The certificate associated with port " + port + " cannot be retrieved.");

# Parse the server's certificate.
cert = parse_der_cert(cert:cert);
if (isnull(cert))
  exit(1, "The certificate associated with port " + port + " cannot be parsed.");

# Extract the Common Names from the certificate.
cns = make_list();

tbs = cert["tbsCertificate"];
subject = tbs["subject"];
foreach field (subject)
{
  if (field[0] != "2.5.4.3")
    continue;
  if ( isnull(field[1]) )
    continue;

  cn = field[1];
  cns = make_list(cns, tolower(cn));
  set_kb_item(name:"X509/" + port + "/CN", value:cn);
}

cns = list_uniq(cns);

# Extract the Alternate Names from the certificate.
ans = make_list();

extensions = tbs["extensions"];
foreach ext (extensions)
{
  if (ext["extnID"] != "2.5.29.17")
    continue;

  foreach value (ext["extnValue"])
  {
    name = value["dNSName"];
    if (isnull(name))
      continue;

    set_kb_item(name:"X509/" + port + "/altName", value:name);
    ans = make_list(ans, tolower(name));
  }
}

ans = list_uniq(ans);

# Combine all the names so we can process them in one go.
cert_names = list_uniq(make_list(cns, ans));
if (max_index(cert_names) <= 0)
  exit(0, "No Common Names and no Subject Alternative Names were found in the certificate associated with port " + port + ".");

# We cannot test if we do not know the hostname, unless we're in PCI
# mode where we're expected to produce a report regardless.
if (!get_kb_item("Settings/PCI_DSS") && max_index(host_names) <= 0)
  exit(1, "No host name is available for the remote target.");

# Compare all names found in the certificate against all names and
# addresses of the host.
foreach cert_name (cert_names)
{
  foreach host_name (host_names)
  {
    # Try an exact match of the names.
    if (cert_name == host_name)
    {
      set_kb_item(name:"X509/" + port + "/hostname_match", value:TRUE);
      exit(0, "The certificate associated with port " + port + " matches one of the hostnames exactly.");
    }

    i = stridx(cert_name, ".");
    if (i == 1 && cert_name[0] == "*")
    {
      # Try a wildcard match of the names.
      j = stridx(host_name, ".");
      if (j >= 0 && substr(host_name, j) == substr(cert_name, i))
      {
        set_kb_item(name:"X509/" + port + "/hostname_match", value:TRUE);
        exit(0, "The certificate associated with port " + port + " matches one of the hostnames with a wildcard.");
      }
    }
    else
    {
      # Try an address-based match of the name.
      if (is_same_host(a:cert_name, fqdn:TRUE))
      {
        set_kb_item(name:"X509/" + port + "/IP_addr_match", value:TRUE);
        exit(0, "The certificate associated with port " + port + " matches the one of the host's addresses exactly.");
      }
    }
  }
}

# If we don't know any names for the host, consider its address as its
# name in the report.
if (max_index(host_names) <= 0)
  host_names = make_list(addr);

# Report our findings.
if (max_index(host_names) > 1)
  s = "s known by Nessus are";
else
  s = " known by Nessus is";

report =
  '\nThe host name' + s + ' :' +
  '\n' +
  '\n  ' + join(sort(host_names), sep:'\n  ') +
  '\n';

if (cns && max_index(cns) > 0)
{
  if (max_index(cns) > 1)
    s = "s in the certificate are";
  else
    s = " in the certificate is";

  report +=
    '\nThe Common Name' + s + ' :' +
    '\n' +
    '\n  ' + join(sort(cns), sep:'\n  ') +
    '\n';
}

if (ans && max_index(ans) > 0)
{
  if (max_index(ans) > 1)
    s = "s in the certificate are";
  else
    s = " in the certificate is";

  report +=
    '\nThe Subject Alternate Name' + s + ' :' +
    '\n' +
    '\n  ' + join(sort(ans), sep:'\n  ') +
    '\n';
}

security_note(port:port, extra:report);