Like a lock that only jams if someone installed an optional, rarely used extra tumbler
CVE-2023-0464 is an algorithmic complexity DoS in OpenSSL's X.509 policy-constraint verification path. It affects OpenSSL 1.0.2 through before 1.0.2zh, 1.1.1 through before 1.1.1u, 3.0.0 through before 3.0.9, and 3.1.0 through before 3.1.1. An attacker needs to supply a malicious certificate chain to an application that performs certificate validation with policy processing enabled, which is *not the default*.
The 7.5/HIGH CVSS score captures the theoretical network DoS outcome, but it badly overstates enterprise reality. The decisive friction is that policy processing is disabled by default and usually only appears in specialized PKI-heavy apps, custom certificate-validation code, or compliance-driven deployments; that cuts the reachable population down hard, and the impact is still availability-only.
3 steps from start to impact.
Find a target that actually uses policy checking
-policy path or the X509_VERIFY_PARAM_set1_policies() API (docs). In practice this is usually custom PKI tooling, certificate gateways, or niche trust-policy workflows rather than ordinary HTTPS serving.- Target application uses vulnerable OpenSSL
- Application accepts untrusted certificate chains
- Policy checking is explicitly enabled
- Policy processing is disabled by default in OpenSSL
- Many common TLS server deployments never invoke this path
- Reachability depends on app behavior, not just the library being installed
Feed a malicious chain into certificate verification
openssl verify against a malicious chain.- Attacker can present or upload a certificate chain to the vulnerable workflow
- Target uses OpenSSL verification on attacker-controlled input
- Many deployments only verify certificates from tightly controlled internal CAs
- Mutual TLS and custom trust workflows are a minority case in most enterprises
Burn CPU in the verifier
- Verification runs synchronously on a request or session path
- Resource limits or watchdogs do not kill the work quickly
- Timeouts, worker isolation, and process supervision often cap blast radius
- Impact is availability-only; there is no direct code execution or data exposure
X509_VERIFY_PARAM_set1_policies(). This sharply narrows your true exposure and keeps the library finding from turning into meaningless fleet churn.- A generic WAF does not help much; the payload is a malicious certificate chain inside a crypto validation workflow, not an HTTP exploit pattern.
- MFA is irrelevant; this is not an authentication bypass or account attack.
- Simple port-based external exposure counts are misleading; an HTTPS listener using OpenSSL is not automatically exploitable unless the app enables policy checking.
The supporting signals.
| In-the-wild status | No public evidence of active exploitation found; not listed in CISA KEV as of today. This is a major downgrade factor. |
|---|---|
| Proof-of-concept availability | Yes. Public PoC references exist, including Trinadh465/Openssl_1.1.1g_CVE-2023-0464 and technical walkthroughs demonstrating verifier CPU exhaustion. |
| EPSS | 0.00734 supplied in your intel; that is very low exploit likelihood relative to internet-priority CVEs. |
| KEV status | Not in CISA KEV; no federal due date, no known ransomware association published by CISA. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H scores the worst-case path as simple unauthenticated network DoS, but it does not encode the biggest real-world constraint: non-default policy checking must be enabled first. |
| Affected versions | Upstream affected trains are 1.0.2 < 1.0.2zh, 1.1.1 < 1.1.1u, 3.0.0 < 3.0.9, and 3.1.0 < 3.1.1. |
| Fixed versions | Upstream fixes landed in 1.0.2zh, 1.1.1u, 3.0.9, and 3.1.1. Distros backported fixes: Debian bullseye 1.1.1n-0+deb11u5, Ubuntu jammy 3.0.2-0ubuntu1.9, Ubuntu focal 1.1.1f-1ubuntu2.18, plus multiple Red Hat and SUSE errata. |
| Exposure/scanning reality | There is no reliable Shodan/Censys-style fingerprint for 'vulnerable to CVE-2023-0464' because this is a library flaw gated by application logic and non-default API use, not a banner-identifiable appliance bug. Expect internet exposure data to be mostly noise. |
| Disclosure | Disclosed 2023-03-22. |
| Reporter | Reported to OpenSSL on 2023-01-12 by David Benjamin (Google); fix developed by Dr Paul Dale. |
Sources.
Why this verdict
- Down from 7.5/HIGH because policy checking is off by default in OpenSSL; the vulnerable path is opt-in.
- Requires attacker-controlled certificate input into verification; installed library presence alone does not create an exploit path.
- Exposure population is narrow and hard to reach externally; this is mostly relevant to PKI-specialized services, custom validators, and niche trust-policy deployments.
- Impact is availability only; there is no credential theft, privilege gain, or code execution.
- No KEV listing, no public exploitation evidence, and very low EPSS all argue against priority patch treatment.
Why not higher?
If this were reachable in default TLS server behavior, the vendor's 7.5 would be fair. But the exploit chain has a major prerequisite that most enterprises do not satisfy: an application must explicitly enable policy processing and then accept attacker-controlled cert chains into that path. That is too much friction for HIGH.
Why not lower?
It is not IGNORE because public PoC material exists and the trigger is remotely deliverable where the feature is in use. In PKI-heavy environments, a single exposed certificate-verification service can still suffer meaningful operational pain, so it belongs in backlog hygiene rather than being dismissed.
Crowdsourced verification payload.
Run this on the target Linux host as a local audit check. Invoke it as bash cve-2023-0464-check.sh with no special privileges required for basic package/version inspection; root helps only if your package manager metadata is restricted.
#!/usr/bin/env bash
# CVE-2023-0464 OpenSSL local exposure check
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
result="UNKNOWN"
reason="Could not determine package/version state"
have_cmd() { command -v "$1" >/dev/null 2>&1; }
ver_ge() {
# returns 0 if $1 >= $2 using sort -V
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]
}
check_upstream_openssl() {
local v
v=$(openssl version 2>/dev/null | awk '{print $2}') || return 1
case "$v" in
3.1.*)
if ver_ge "$v" "3.1.1"; then result="PATCHED"; reason="Upstream OpenSSL $v >= 3.1.1"; return 0; else result="VULNERABLE"; reason="Upstream OpenSSL $v < 3.1.1"; return 0; fi ;;
3.0.*)
if ver_ge "$v" "3.0.9"; then result="PATCHED"; reason="Upstream OpenSSL $v >= 3.0.9"; return 0; else result="VULNERABLE"; reason="Upstream OpenSSL $v < 3.0.9"; return 0; fi ;;
1.1.1*)
local suffix
suffix=${v#1.1.1}
if [[ -z "$suffix" ]]; then result="VULNERABLE"; reason="Upstream OpenSSL 1.1.1 baseline is older than 1.1.1u"; return 0; fi
local letter=${suffix:0:1}
if [[ "$letter" =~ [u-z] ]]; then result="PATCHED"; reason="Upstream OpenSSL $v >= 1.1.1u"; else result="VULNERABLE"; reason="Upstream OpenSSL $v < 1.1.1u"; fi
return 0 ;;
1.0.2*)
local suffix
suffix=${v#1.0.2}
if [[ "$suffix" == zh* ]]; then result="PATCHED"; reason="Upstream OpenSSL $v >= 1.0.2zh"; else result="VULNERABLE"; reason="Upstream OpenSSL $v < 1.0.2zh"; fi
return 0 ;;
esac
return 1
}
check_dpkg() {
local pkgver osrel
pkgver=$(dpkg-query -W -f='${Version}' openssl 2>/dev/null) || return 1
osrel=$(grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"')
case "$osrel" in
jammy)
if dpkg --compare-versions "$pkgver" ge "3.0.2-0ubuntu1.9"; then result="PATCHED"; reason="Ubuntu jammy openssl $pkgver includes fix"; else result="VULNERABLE"; reason="Ubuntu jammy openssl $pkgver older than fixed 3.0.2-0ubuntu1.9"; fi ;;
focal)
if dpkg --compare-versions "$pkgver" ge "1.1.1f-1ubuntu2.18"; then result="PATCHED"; reason="Ubuntu focal openssl $pkgver includes fix"; else result="VULNERABLE"; reason="Ubuntu focal openssl $pkgver older than fixed 1.1.1f-1ubuntu2.18"; fi ;;
bionic)
if dpkg --compare-versions "$pkgver" ge "1.1.1-1ubuntu2.1~18.04.22"; then result="PATCHED"; reason="Ubuntu bionic openssl $pkgver includes fix"; else result="VULNERABLE"; reason="Ubuntu bionic openssl $pkgver older than fixed 1.1.1-1ubuntu2.1~18.04.22"; fi ;;
bullseye)
if dpkg --compare-versions "$pkgver" ge "1.1.1n-0+deb11u5"; then result="PATCHED"; reason="Debian bullseye openssl $pkgver includes fix"; else result="VULNERABLE"; reason="Debian bullseye openssl $pkgver older than fixed 1.1.1n-0+deb11u5"; fi ;;
*)
return 1 ;;
esac
return 0
}
check_rpm() {
local pkgver
pkgver=$(rpm -q --qf '%{VERSION}-%{RELEASE}\n' openssl 2>/dev/null) || return 1
# RHEL/SUSE often backport fixes without obvious upstream-style version jumps.
# If rpm-based and package metadata is present, prefer errata-driven validation by your scanner/SBOM.
result="UNKNOWN"
reason="RPM package found ($pkgver). Verify against vendor errata/backports; version string alone may be misleading."
return 0
}
if have_cmd dpkg-query && check_dpkg; then
:
elif have_cmd rpm && check_rpm; then
:
elif have_cmd openssl && check_upstream_openssl; then
:
fi
printf '%s - %s\n' "$result" "$reason"
case "$result" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esac