There’s a class of TLS bugs that are worse than memory corruption, because the server tells you it’s working.
CVE-2026-115881 hits GoAhead 3.4.5 through 6.1.0 when built with its OpenSSL-compatible backend and configured for mutual TLS with a CRL. The server loads the revocation list at startup, prints no error, and goes about its business. When a client presents a certificate that’s on the CRL, OpenSSL verifies the signature chain, the issuer, the expiry — and accepts it. The CRL is sitting in memory. It’s just never consulted.
GoAhead loads CRL → OpenSSL store populated → X509_V_FLAG_CRL_CHECK not set → revoked cert accepted
The severity rating is 9.1. The fix is GoAhead 6.1.1, released August 2, 2026 and publicly documented October 12. As of October 27, the CVE identifiers hadn’t propagated through the public CVE and NVD databases — EmbedThis had requested them.
Why This Class of Bug Is Dangerous
It’s the configuration that looks right. An operator configures mTLS on a gateway appliance or an edge controller. They point the CRL file. The server starts, the CRL loads, there’s no warning. Months later, an employee leaves, their device certificate gets revoked — and the device still connects. The operator’s monitoring shows successful handshakes from a valid (but revoked) identity. Nothing breaks. Nothing alerts.
This isn’t unique to GoAhead. I’ve seen it in:
- Custom OpenSSL wrappers that call
X509_STORE_add_cert()but skipX509_STORE_set_flags() - Nginx modules that load a CRL via
SSL_CTX_load_verify_locations()without setting verification flags - Python’s
ssl.SSLContext.load_verify_locations()followed bycheck_hostname = Truebut no CRL enforcement
The pattern: load the list, forget to turn on the check.
What GoAhead Specifically Does Wrong
In the OpenSSL backend, GoAhead calls SSL_CTX_load_verify_locations() to load the trusted CA bundle and the CRL file. OpenSSL’s API treats these as a single file — you pass the CRL file path and OpenSSL parses it into the store. But loading into the store is not the same as checking against it.
OpenSSL’s verification process uses flags in the X509_VERIFY_PARAM to decide which checks to perform. The X509_V_FLAG_CRL_CHECK flag is what tells OpenSSL to actually validate the presented certificate against the loaded CRLs. Without it, the store is populated but the verification callback never looks at it.
FoxIO’s Sébastien Féry reproduced this against GoAhead 6.1.0 with OpenSSL 3.6.2. A revoked certificate signed by a trusted CA completed the mTLS handshake with issuer verification enabled. The server logged the connection as successful. The CRL sat in memory, unconsulted.
Detection: What to Look For
Because this is a silent failure, detection requires checking the configuration, not the logs. Here’s what I look for:
- Software inventory: Find GoAhead instances (JA4Scan fingerprints help identify the TLS backend)
- CRL configuration: Does the config specify a CRL file?
- Backend: Is it using the OpenSSL-compatible backend or Mbed TLS? (Mbed TLS checks CRLs via a separate path — unaffected)
- mTLS setup: Is
require_client_certenabled? Is issuer verification on?
If all four are true, there’s a good chance you’re running with a CRL that loads but doesn’t enforce.
The Second Bug: Session Decryption
CVE-2026-115881 gets the attention, but GoAhead has another flaw (CVSS 5.9) in Mbed TLS builds: a coding error weakens the encryption parameters on some TLS 1.2 connections, allowing recorded sessions to be decrypted offline. This only works if the client accepts the flawed parameters — so it’s a downgrade attack that requires a willing victim.
And CVE-2026-115884 (CVSS 7.4) affects LibreSSL 3.2.x / 3.3.2–3.3.6 builds: a client certificate chain longer than the limit is accepted when issuer checking is off. The length cap is the only safeguard.
Why Embedded Devices Care Most
GoAhead runs on routers, industrial controllers, medical devices, network switches — appliances that operators configure once and forget for years. The CRL gets set up at deployment, the server starts cleanly, and nobody thinks about it until they need to revoke a certificate and wonder why it still connects.
By the time the fix lands, the device vendor has to push a firmware update. Some vendors patch promptly. Others never do. The CRL keeps loading. The check stays off.
The Lesson
This isn’t a GoAhead bug. It’s an OpenSSL API design issue — X509_STORE_add_cert() and SSL_CTX_load_verify_locations() load data, but verification flags control what gets checked. If you load a CRL and don’t set CRL_CHECK, the data is there and irrelevant.
The same pattern shows up in PHP workers (stale state), in proxy configurations (loaded ACLs, unenforced rules), in database connection pools (idle connections never validated). Loaded ≠ enforced. Always verify the enforcement.
If you’re running GoAhead with mTLS and a CRL, check the backend and verify a revoked certificate is actually rejected. Don’t trust the startup log.