The Exploit
An unauthenticated remote attacker can bypass OCSP certificate revocation checks on NGINX Stream (TCP/UDP) TLS listeners. The following curl command demonstrates the bypass using a revoked client certificate against an NGINX stream block configured with ssl_verify_client on and ssl_ocsp on.
curl -v \
--cert revoked-client.crt \
--key revoked-client.key \
--resolve stream.example.com:443:192.0.2.10 \
"https://stream.example.com:443/"
When the attacker sends this request with a revoked certificate, NGINX accepts the TLS handshake and proxies the connection normally instead of rejecting it. The server log will show "client SSL certificate verify error: certificate revoked" yet the connection succeeds, allowing the attacker to interact with the backend service using a revoked identity.
What the Patch Did
Before:
long rc;
X509 *cert;
ngx_int_t rv;
ngx_connection_t *c;
ngx_stream_ssl_srv_conf_t *sscf;
...
X509_free(cert);
}
return NGX_OK;
After:
long rc;
X509 *cert;
ngx_int_t rv;
const char *str;
ngx_connection_t *c;
ngx_stream_ssl_srv_conf_t *sscf;
...
X509_free(cert);
}
if (ngx_ssl_ocsp_get_status(c, &str) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client SSL certificate verify error: %s", str);
ngx_ssl_remove_cached_session(c->ssl->session_ctx,
(SSL_get0_session(c->ssl->connection)));
return NGX_ERROR;
}
}
return NGX_OK;
The patch adds a call to ngx_ssl_ocsp_get_status() after the existing certificate verification occurs. This function checks the OCSP stapling result for the client certificate and returns an error if the certificate status is anything other than "good", such as "revoked" or "unknown". The patch also introduces ngx_ssl_remove_cached_session() to purge any cached TLS session that might have been established before the revocation was detected.
Root Cause
CWE-299: Improper Check for Certificate Revocation. The vulnerability exists because the ngx_stream_ssl_module only validated the certificate chain and expiry but never queried the OCSP response status obtained during the TLS handshake for stream connections. The dataflow is: the client presents a certificate → NGINX performs standard X.509 validation (chain, expiry, signature) → NGINX performs the OCSP check but does not inspect the result → connection proceeds to NGX_OK → attacker gains unauthorized access. The ssl_ocsp on directive was implemented half-completely for streams — it queried the OCSP responder but discarded the result, unlike the HTTP sibling module ngx_http_ssl_module which already had this check.
Why It Works
The single load-bearing line is if (ngx_ssl_ocsp_get_status(c, &str) != NGX_OK). If an attacker removes this one line from the patched binary (or if a developer forgets to backport it), the bug is fully exploitable again because ngx_ssl_ocsp_get_status() is the only function that translates the internal OCSP verification state into a return code the module can act upon. The engineer added the second call ngx_ssl_remove_cached_session() as a defensive cleanup — it prevents stale cached sessions from re-establishing the vulnerability after the OCSP status changes from "good" to "revoked" (for example, when a CA updates their revocation list). Without the session cache removal, a previously-validated certificate that later gets revoked could still be used by reconnecting clients if the session is resumed.
Hardening Checklist
- Always validate OCSP stapling results for client certificates by calling the appropriate OpenSSL/NGINX API equivalent after certificate chain validation in both HTTP and Stream contexts.
- Implement session cache invalidation when a certificate revocation is detected to prevent session resumption attacks — use
SSL_CTX_remove_session()or the NGINX equivalentngx_ssl_remove_cached_session(). - Unify security controls between HTTP and Stream modules — audit for discrepancies where the Stream (layer 4) module lacks security checks present in the HTTP (layer 7) module, as these are common blind spots in NGINX configurations.
- Log all certificate verification failures at minimum
NGX_LOG_INFOand ensure these logs reach a central monitoring system for alerting on unexpected certificate behavior. - Test revocation handling explicitly during certificate lifecycle testing by generating test certificates with OCSP responder simulations to confirm revoked certificates are always rejected.
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-28755