This is a dead ID badge on the front door, not a hidden tunnel through the vault
Plugin 15901 fires when a scanned SSL/TLS-enabled service presents an X.509 certificate whose notAfter date is already in the past. There is no vulnerable software version range here in the normal CVE sense; *any* product version can trigger it if the currently deployed certificate is expired, including web servers, VPN portals, load balancers, middleware, management interfaces, and internal apps.
Tenable labels it Medium because expired certificates break validation and can undermine integrity if users or clients are trained to click through warnings. For enterprise patch prioritization, that overstates the *exploitability*: an expired cert is mostly an availability, trust, and compliance failure unless an attacker also has a man-in-the-middle position or can trick users into bypassing validation.
4 steps from start to impact.
Enumerate the TLS service with openssl s_client or zgrab2
notAfter, the condition is observable immediately without credentials or exploitation.- Network reachability to the TLS service
- A service actually presenting an expired certificate
- This is only discovery; it does not grant code execution, auth bypass, or data access
- Many findings are on internal-only or admin-only services with no external user path
openssl, load balancers, and synthetic monitoring all detect this reliably.Client validation fails in the browser, agent, or API consumer
- A user, browser, script, or service attempts to connect
- The client performs normal certificate validation
- Secure clients block the session rather than enabling attacker progress
- Well-built API clients and service meshes fail closed instead of silently accepting the cert
Attacker abuses the exception path with mitmproxy or bettercap
- Attacker can intercept or redirect traffic
- Users or clients are willing or configured to bypass certificate errors
- Requires prior network position or another foothold
- HSTS, pinned certs, strict TLS libraries, corporate proxies, and managed browsers often block the bypass
Impact is trust erosion or outage before it is compromise
- Business process depends on the affected service
- Operational teams use insecure workarounds
- No direct payload delivery path exists from the expired certificate alone
- Blast radius is limited to the specific service or trust path presenting the bad certificate
The supporting signals.
| In-the-wild status | No active exploitation evidence as a standalone issue. This is not a CVE-class software flaw and there is no known campaign category where 'expired cert only' is the intrusion mechanism. |
|---|---|
| Proof-of-concept availability | Trivial to verify, not really exploitable. Any TLS client such as openssl s_client, a browser, or zgrab2 can confirm the condition, but that is validation failure testing, not weaponized exploit code. |
| EPSS | Not applicable. There is no CVE, so no FIRST EPSS probability exists. |
| KEV status | Not applicable. Plugin 15901 is a Nessus finding, not a CISA KEV-listed CVE. |
| Vendor score and vector | Tenable assigns Medium / 5.3 with CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N, manually justified because expired certificates cannot be validated. |
| Affected scope | Any product version can be affected if it currently presents an expired SSL/TLS certificate. This is certificate state, not a buggy release train. |
| Fixed state | No patched software version. The condition clears when the presented leaf cert and chain are renewed/replaced and the service is reloaded or restarted as required. |
| Exposure and scanning reality | Public-internet search engines like Censys index certificate validity metadata and explicitly note that expired certificates on live hosts can cause service interruption. But this plugin also commonly hits internal-only services, so fleet exposure is highly environment-specific. |
| Compliance and business impact | Tenable documents plugin 15901 as part of PCI SSL/TLS validity and expiration checks; an expired cert can become an audit failure even when exploitability is low. |
| Disclosure / origin | Nessus plugin 15901 was published on 2004-12-03 by Tenable. That date reflects scanner content publication, not disclosure of a software vulnerability. |
noisgate verdict.
The decisive friction is that an expired certificate is not an exploit primitive: it does not hand an attacker execution, access, or privilege by itself. To get from this finding to compromise, the attacker usually needs a second major assumption such as MITM position, client misconfiguration, or users conditioned to bypass TLS warnings.
Why this verdict
- Start at 5.3, then subtract hard because this is not a software vuln. Tenable's baseline treats failed certificate validation as integrity impact, but there is no memory corruption, auth bypass, sandbox escape, or direct attacker-controlled state transition here.
- Second-stage attacker position required. To weaponize this into credential theft or interception, the adversary typically needs unauthenticated remote reach *plus* a MITM position, DNS influence, hostile Wi-Fi, or a user willing to click through warnings. Each of those assumptions sharply narrows real-world exploitability.
- Reachable population is broader than external exposure but narrower than the score implies. Plugin
15901hits internet-facing apps, internal apps, appliances, and management planes alike; many detections are operationally important but never exposed to arbitrary attackers, which compounds downward pressure on severity.
Why not higher?
There is no credible single-step path from 'expired cert detected' to host compromise. Modern browsers and TLS libraries are designed to stop at this exact boundary, so the finding more often causes outages and audit pain than attacker success.
Why not lower?
This is not pure noise. Expired certificates on production services break authentication guarantees, generate user warning fatigue, and can push teams into unsafe exceptions like disabling validation or bypassing browser warnings. On customer-facing or identity-adjacent systems, that operational pressure is worth tracking even if it is not emergency patch material.
What to do — in priority order.
- Turn on certificate-expiry monitoring — Deploy or tighten monitoring at the load balancer, reverse proxy, PKI platform, or synthetic probe layer so you alert on
30/14/7/3/1day thresholds before Nessus finds it. For a LOW verdict there is no SLA — treat as backlog hygiene, but do this in the next routine cert-management cycle because prevention is far cheaper than cleanup. - Block insecure client exceptions — Enforce managed browser and API-client policies that prohibit click-through on invalid certs and ban flags like
--insecure,curl -k, or custom trust-all code paths. For a LOW verdict there is no SLA — treat as backlog hygiene, but close exception paths before they become the real vulnerability. - Automate certificate renewal — Use ACME, centralized PKI automation, or cert lifecycle tooling so replacement and service reload happen without calendar-driven manual work. For a LOW verdict there is no SLA — treat as backlog hygiene, but this is the durable fix for large fleets.
- Prioritize internet-facing and identity services first — If the expired cert sits on SSO, VPN, mail, customer portals, or externally reachable apps, treat it as an *operations outage* and renew it immediately even though the security severity is LOW. The reassessment only says 'don't burn emergency vuln-patching capacity across the whole fleet.'
- Suppressing the Nessus finding without renewing the certificate; you only hide the symptom while users and clients still hit validation failures.
- Disabling certificate validation in browsers, agents, or scripts; that removes the very control that prevents an expired cert from becoming a real interception risk.
- Relying on EDR alone; endpoint tools do not fix broken PKI state or prevent unsafe manual click-through decisions at the browser and middleware layers.
Crowdsourced verification payload.
Run this from an auditor workstation, CI job, or scanner utility host that can reach the target service over the network. Invoke it as python3 check_cert_expiry.py example.com 443 or python3 check_cert_expiry.py 10.10.10.25 8443 app.internal.example; it needs no privileges beyond outbound TCP connectivity.
#!/usr/bin/env python3
# check_cert_expiry.py
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import sys
import ssl
import socket
from datetime import datetime, timezone
TIMEOUT = 8
def parse_args(argv):
if len(argv) < 3 or len(argv) > 4:
print("UNKNOWN - usage: python3 check_cert_expiry.py <host> <port> [sni]", flush=True)
sys.exit(2)
host = argv[1]
try:
port = int(argv[2])
except ValueError:
print("UNKNOWN - port must be an integer", flush=True)
sys.exit(2)
sni = argv[3] if len(argv) == 4 else host
return host, port, sni
def get_peer_cert(host, port, sni):
ctx = ssl.create_default_context()
# We want to inspect the certificate even if it is expired or otherwise invalid.
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with socket.create_connection((host, port), timeout=TIMEOUT) as sock:
with ctx.wrap_socket(sock, server_hostname=sni) as tls:
cert = tls.getpeercert()
cipher = tls.cipher()
version = tls.version()
return cert, cipher, version
def main():
host, port, sni = parse_args(sys.argv)
try:
cert, cipher, version = get_peer_cert(host, port, sni)
except ssl.SSLError as e:
print(f"UNKNOWN - TLS handshake failed: {e}", flush=True)
sys.exit(2)
except OSError as e:
print(f"UNKNOWN - connection failed: {e}", flush=True)
sys.exit(2)
except Exception as e:
print(f"UNKNOWN - unexpected error: {e}", flush=True)
sys.exit(2)
if not cert:
print("UNKNOWN - no certificate presented by remote service", flush=True)
sys.exit(2)
not_after_raw = cert.get("notAfter")
subject = cert.get("subject", ())
issuer = cert.get("issuer", ())
if not not_after_raw:
print("UNKNOWN - certificate missing notAfter field", flush=True)
sys.exit(2)
try:
# Typical format: 'Jun 14 23:59:59 2025 GMT'
not_after = datetime.strptime(not_after_raw, "%b %d %H:%M:%S %Y %Z").replace(tzinfo=timezone.utc)
except ValueError:
print(f"UNKNOWN - unable to parse notAfter: {not_after_raw}", flush=True)
sys.exit(2)
now = datetime.now(timezone.utc)
cn = ""
try:
for rdn in subject:
for key, value in rdn:
if key == "commonName":
cn = value
raise StopIteration
except StopIteration:
pass
issuer_cn = ""
try:
for rdn in issuer:
for key, value in rdn:
if key == "commonName":
issuer_cn = value
raise StopIteration
except StopIteration:
pass
meta = f"host={host} port={port} sni={sni} tls={version} cipher={cipher[0] if cipher else 'unknown'} cn={cn or 'unknown'} issuer={issuer_cn or 'unknown'} notAfter={not_after.isoformat()}"
if now > not_after:
print(f"VULNERABLE - expired certificate presented; {meta}", flush=True)
sys.exit(1)
else:
days_left = (not_after - now).days
print(f"PATCHED - certificate currently valid ({days_left} day(s) remaining); {meta}", flush=True)
sys.exit(0)
if __name__ == "__main__":
main()
If you remember one thing.
Sources
- Tenable Nessus Plugin 15901 - SSL Certificate Expiry
- Tenable whitepaper - PCI / SSL certificate validity and expiration checks
- RFC 5280 - X.509 certificate validity period (`notBefore`/`notAfter`)
- MDN - insecure certificate error caused by expired or invalid TLS certs
- Mozilla Support - browsers stop connecting when certificate validity cannot be verified
- Censys ASM Host Assets - expired certificates on live hosts may cause service interruptions
- Censys Certificates dataset - certificate trust and validity metadata
- DigiCert FAQ - TLS/SSL certificate validity lifetimes and expiration context
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.