What Is CVE-2026-94419?
Vulnerability Overview
CVE-2026-94419 represents a critical authentication bypass vulnerability in wolfSSL’s TLS 1.2 and DTLS 1.2 session resumption mechanism. The flaw allows attackers to poison a process-global session cache, enabling server impersonation without valid certificate validation.
The vulnerability stems from insufficient validation when client applications reference and write session entries to the shared cache. When compiled without protective build flags, wolfSSL returns lightweight session references rather than isolated copies. An attacker can craft a malicious TLS 1.2 Session ID to collide with cached entries belonging to legitimate servers, overwriting the stored master secret and cipher suite without any peer identity verification.
During subsequent connection attempts to the legitimate target server, the client performs an abbreviated TLS handshake—a protocol variant that omits certificate exchange. Because no certificate is transmitted, wolfSSL skips domain name validation and X.509 chain verification entirely, allowing the attacker to impersonate the target endpoint.
Affected wolfSSL Versions
Vulnerable Range: wolfSSL v5.3.0 through v5.9.2 (inclusive)
The vulnerability requires a specific combination of conditions:
- Application uses legacy session reference APIs (
wolfSSL_get_session()orSSL_get_session()) - Build configuration does not define protective macros
- TLS 1.2 or DTLS 1.2 protocol is in use
Vulnerable Build Configurations
The flaw manifests only when all four of these preprocessor macros remain undefined during compilation:
NO_SESSION_CACHE_REFNO_SESSION_CACHENO_CLIENT_CACHETITAN_SESSION_CACHE
Common vulnerable build invocations include:
./configure
./configure --enable-opensslextra
./configure --enable-opensslall
Why Standard Builds Are Protected
Fifteen standard integration configurations explicitly define NO_SESSION_CACHE_REF by default, providing automatic protection:
--enable-all--enable-distro--enable-curl--enable-nginx--enable-haproxy--enable-stunnel--enable-wpas
Additionally, low-resource builds disable client session caching entirely:
--enable-leantls--enable-leanpsk--enable-lowresource--enable-tinytls13
TLS 1.3 implementations are structurally immune due to client-chosen cache keys, as are applications using wolfSSL_get1_session() (which returns isolated copies rather than references).
How the Cache Poisoning Attack Works
Session Reference Mechanism
When wolfSSL compiles without NO_SESSION_CACHE_REF, the wolfSSL_get_session() API returns a lightweight handle rather than a complete session object. This handle contains:
Handle = {row_index, cache_index, hash(sessionID)}
The handle acts as a pointer into the process-global SessionCache structure. When an application later calls wolfSSL_set_session() with this handle, wolfSSL dereferences it using ClientSessionToSession(), which validates the lookup only by comparing the stored session ID hash.
This design choice prioritizes performance over isolation—the same cache serves all WOLFSSL_CTX objects within a single process.
Cache Collision and Overwrite Process
In TLS 1.2 and DTLS 1.2, the server selects and transmits the Session ID in cleartext within the ServerHello message. An attacker exploits this by:
- Crafting a collision: The malicious server sends a Session ID specifically designed to match the cache index/hash of a legitimate target server’s cached session
- Triggering the write path:
AddSessionToCache()processes the incoming session data - Bypassing validation: The write path performs no checks on:
- Peer identity verification
- Server ID comparison
WOLFSSL_CTXisolation
- Overwriting the entry: The attacker’s master secret, cipher suite, and protocol version replace the legitimate cached values
The client application’s existing handle continues pointing to the same cache index, now containing poisoned credentials.

Authentication Bypass During Abbreviated Handshakes
When the application later initiates a connection to the legitimate target server using the poisoned handle:
- Abbreviated handshake initiation: wolfSSL recognizes the cached session and sends an abbreviated handshake request
- Certificate omission: Per TLS 1.2 specification, abbreviated handshakes omit the
Certificatemessage - Validation bypass: Because no certificate is exchanged:
wolfSSL_check_domain_name()is never called- X.509 certificate chain validation is skipped
- Public key infrastructure checks are bypassed
- Successful impersonation: The client completes the handshake using the attacker’s poisoned master secret and accepts the malicious endpoint as legitimate
Cross-Context Exploitation
The process-global nature of the session cache creates a critical isolation failure. An attacker interacting with one WOLFSSL_CTX can poison sessions belonging to completely separate contexts within the same application process. This enables:
- Targeting of high-value connections (API endpoints, authentication servers) through low-value connections (public services)
- Lateral movement within multi-service applications
- Persistence across distinct application components
Real-World Attack Scenarios
Man-in-the-Middle Session Hijacking
Scenario Setup:
- Enterprise application manages TLS 1.2 connections to multiple endpoints (payment processor, identity provider, internal API)
- All connections share a single wolfSSL instance and process-global session cache
- Application uses legacy session flow:
wolfSSL_get_session()→ store →wolfSSL_set_session()
Attack Execution:
- Initial compromise: Attacker gains network position (compromised router, BGP hijacking, DNS poisoning) or controls a legitimate but untrusted endpoint
- Cache injection: When the application connects to the attacker’s endpoint, the malicious server sends a TLS 1.2 Session ID crafted to collide with the cached entry for
api.internal.company.com - Cache overwrite:
AddSessionToCache()overwrites the legitimate API server’s cached master secret with the attacker’s credentials - Resumption attack: Application initiates connection to
api.internal.company.comusing its cached session handle - Validation bypass: Client sends abbreviated handshake to attacker (or attacker intercepts via network position). No certificates are validated. Client accepts attacker as legitimate API server.
- Credential theft: Attacker captures authentication tokens, API keys, or sensitive data transmitted over the compromised connection
Persistence Window: The poisoned cache entry remains valid for up to 500 seconds (default timeout), enabling multiple attack attempts within a single session window.
Multi-Endpoint Application Targeting
Scenario:
- SaaS application connects to multiple third-party APIs (Stripe, Twilio, AWS)
- Attacker controls or compromises one low-security endpoint (public webhook receiver, analytics service)
- Attacker crafts Session IDs to collide with cached entries for high-value targets
Attack Flow:
Attacker's Endpoint → Cache Poisoning → Legitimate API Connection
(Low Security) (Cross-Context) (High Value)
↓
Credential Theft
Data Exfiltration
Service Disruption
Persistence and Timing Windows
The 500-second default cache timeout creates a practical exploitation window. An attacker can:
- Poison the cache during off-peak hours
- Wait for legitimate application activity to trigger the abbreviated handshake
- Capture credentials or data without immediate detection
- Repeat the attack multiple times within the persistence window
Threat Assessment and Risk Factors
Severity Classification
CVSS Score Indicators:
- Attack Vector: Network (requires network access)
- Attack Complexity: Low (no special conditions required)
- Privileges Required: None (unauthenticated attacker)
- User Interaction: None
- Scope: Changed (affects other contexts within the process)
- Confidentiality Impact: High (master secrets exposed)
- Integrity Impact: High (session hijacking enables data modification)
- Availability Impact: High (service disruption possible)
Severity Level: Critical
Protocol Scope Limitations
The vulnerability is strictly limited to:
- TLS 1.2 with server-selected Session IDs
- DTLS 1.2 with server-selected Session IDs
Unaffected protocols:
- TLS 1.3: Uses client-chosen cache keys, rendering server-side collision attacks impossible
- Ticket-based resumption: When ServerHello contains an empty session ID, the client chooses the cache key
- Server ID lookups: Applications using
wolfSSL_SetServerID()bypass the vulnerable code path
Cross-Context Blast Radius
The process-global cache creates an unusually broad attack surface:
| Attack Vector | Impact | Severity |
|---|---|---|
| Single compromised endpoint | Poisons sessions for all contexts | Critical |
| Public service compromise | Enables targeting of internal APIs | Critical |
| Timing-based attacks | Multiple poisoning attempts within window | High |
| Multi-service applications | Lateral movement between components | Critical |
Authentication Integrity Impact
The vulnerability completely degrades authentication integrity during abbreviated handshakes:
- Certificate validation: Bypassed (no certificate transmitted)
- Domain name verification: Bypassed (no domain check performed)
- Public key verification: Bypassed (no public key available)
- Chain of trust: Bypassed (no certificate chain to validate)
An attacker achieves complete impersonation without triggering any certificate error alerts or warnings.
Detecting Vulnerable Configurations
Source Code and Build Auditing
Step 1: Identify Legacy API Usage
Search application source code for vulnerable session handling patterns:
grep -r "wolfSSL_get_session\|SSL_get_session" src/
grep -r "wolfSSL_set_session\|SSL_set_session" src/
Vulnerable pattern:
// VULNERABLE: Returns reference handle
SSL_SESSION *sess = SSL_get_session(ssl);
// ... later ...
SSL_set_session(ssl2, sess); // Reuses reference
Safe pattern:
// SAFE: Returns isolated copy
SSL_SESSION *sess = SSL_get1_session(ssl);
// ... later ...
SSL_set_session(ssl2, sess); // Uses independent copy
SSL_SESSION_free(sess); // Cleanup
Step 2: Verify Build Configuration
Examine build scripts and configuration:
# Check CFLAGS for protective macros
grep -E "NO_SESSION_CACHE_REF|NO_SESSION_CACHE|NO_CLIENT_CACHE" Makefile
grep -E "NO_SESSION_CACHE_REF|NO_SESSION_CACHE|NO_CLIENT_CACHE" configure.ac
# Verify configure invocation
cat build.log | grep "./configure"
Step 3: Binary Analysis
For compiled binaries without source access:
# Check for presence of vulnerable functions
nm -D libwolfssl.so | grep -E "ClientSessionToSession|AddSessionToCache"
# Verify cache version indicator
strings libwolfssl.so | grep "WOLFSSL_CACHE_VERSION"
API Usage Pattern Identification
Audit application code for session management patterns:
Vulnerable Pattern 1: Reference Reuse
SSL_SESSION *cached = SSL_get_session(conn1);
SSL_set_session(conn2, cached); // VULNERABLE
Vulnerable Pattern 2: Global Session Storage
static SSL_SESSION *global_session = NULL;
global_session = SSL_get_session(conn); // VULNERABLE
SSL_set_session(other_conn, global_session);
Vulnerable Pattern 3: Multi-Context Sharing
SSL_CTX *ctx1 = SSL_CTX_new(...);
SSL_CTX *ctx2 = SSL_CTX_new(...);
SSL *ssl1 = SSL_new(ctx1);
SSL *ssl2 = SSL_new(ctx2);
SSL_SESSION *sess = SSL_get_session(ssl1);
SSL_set_session(ssl2, sess); // VULNERABLE: Cross-context
Cache Version Monitoring
Inspect persistent session cache structures for version indicators:
// Unpatched implementation
#define WOLFSSL_CACHE_VERSION 2
// Patched implementation
#define WOLFSSL_CACHE_VERSION 3
Check serialized cache files:
# Extract version from cache file
hexdump -C session.cache | head -20
# Look for version marker (typically at offset 0x00-0x04)
Network Traffic Indicators
Monitor for suspicious TLS patterns:
- Identical Session IDs across different targets:
Connection A → Server X → Session ID: 0xABCD1234
Connection B → Server Y → Session ID: 0xABCD1234 ← Suspicious
- Abbreviated handshakes to unexpected targets:
- Monitor for ClientHello messages with session ID set
- Track which servers receive abbreviated handshakes
- Alert on abbreviated handshakes to new/untrusted servers
- Session ID collision patterns:
- Same client process sending identical Session IDs to different servers
- Session IDs matching across unrelated application contexts
Remediation and Mitigation Strategies
Immediate Upgrade Path
Primary Fix: Upgrade to wolfSSL v5.9.3 or Later
The official patch introduces per-write generation counters and increments WOLFSSL_CACHE_VERSION from 2 to 3:
# Download and build patched version
wget https://github.com/wolfSSL/wolfssl/archive/v5.9.3.tar.gz
tar xzf v5.9.3.tar.gz
cd wolfssl-5.9.3
./configure --enable-distro
make
sudo make install
Verification:
# Confirm version
wolfssl-config --version
# Should output: 5.9.3 or later
# Verify cache version
strings /usr/local/lib/libwolfssl.so | grep WOLFSSL_CACHE_VERSION
# Should show version 3
Timeline:
- Immediate: Test patched version in development environment
- Within 48 hours: Deploy to staging
- Within 1 week: Roll out to production
Application-Level Workarounds
Workaround 1: Migrate to Deep-Copy API
Replace all wolfSSL_get_session() calls with wolfSSL_get1_session():
// BEFORE (Vulnerable)
SSL_SESSION *sess = SSL_get_session(ssl);
SSL_set_session(ssl2, sess);
// AFTER (Safe)
SSL_SESSION *sess = SSL_get1_session(ssl); // Returns isolated copy
SSL_set_session(ssl2, sess);
SSL_SESSION_free(sess); // Must free the copy
Benefits:
- Immediate protection without recompilation
- No cache reference sharing
- Each session is independent
Implementation Checklist:
- [ ] Audit all
SSL_get_session()calls - [ ] Replace with
SSL_get1_session() - [ ] Add
SSL_SESSION_free()calls - [ ] Test session resumption functionality
- [ ] Verify no memory leaks
Workaround 2: Use Server ID Lookups
Replace session reference-based resumption with Server ID:
// BEFORE (Vulnerable)
SSL_SESSION *sess = SSL_get_session(ssl1);
SSL_set_session(ssl2, sess);
// AFTER (Safe)
unsigned char server_id[] = "api.example.com";
SSL_SetServerID(ssl2, server_id, sizeof(server_id) - 1, 0);
// Session lookup uses server ID, not global cache reference
Benefits:
- Isolates sessions by server identity
- Prevents cross-context poisoning
- No API changes required for session storage
Compile-Time Configuration Options
Option 1: Enable Protective Build Flag
./configure --enable-distro
# Automatically sets NO_SESSION_CACHE_REF
Option 2: Manual Flag Definition
./configure CFLAGS="-DNO_SESSION_CACHE_REF"
make
sudo make install
Option 3: Disable Client Session Caching Entirely
./configure --enable-leantls
# Disables all client-side session caching
Option 4: Docker Build Example
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y build-essential
WORKDIR /build
RUN wget https://github.com/wolfSSL/wolfssl/archive/v5.9.3.tar.gz && \
tar xzf v5.9.3.tar.gz && \
cd wolfssl-5.9.3 && \
./configure --enable-distro && \
make && \
make install
RUN ldconfig
API Migration Guidance
Step-by-Step Migration Process:
- Inventory Phase:
grep -r "SSL_get_session\|wolfSSL_get_session" . > session_usage.txt
# Document all occurrences
- Analysis Phase:
- Identify session storage patterns
- Determine session lifetime requirements
- Map session usage across contexts
- Implementation Phase:
// Pattern 1: Simple replacement
- SSL_SESSION *s = SSL_get_session(ssl);
+ SSL_SESSION *s = SSL_get1_session(ssl);
// Pattern 2: Add cleanup
+ if (s) SSL_SESSION_free(s);
// Pattern 3: Update storage
- static SSL_SESSION *cached;
- cached = SSL_get_session(ssl);
+ SSL_SESSION *cached = SSL_get1_session(ssl);
+ // Use immediately, don't store
- Testing Phase:
- Unit tests for session resumption
- Integration tests across contexts
- Load testing for memory leaks
- Security testing for cache isolation
- Deployment Phase:
- Staged rollout to production
- Monitor for session resumption failures
- Verify no performance degradation
TLS 1.3 and Alternative Protocols
Why TLS 1.3 Is Unaffected
TLS 1.3 fundamentally restructures session resumption, eliminating the vulnerability:
TLS 1.2 Session ID (Vulnerable):
Server selects Session ID → Transmitted in cleartext →
Attacker can predict/collide → Cache poisoning possible
TLS 1.3 Session Ticket (Safe):
Server creates encrypted ticket → Client stores ticket →
Client chooses cache key → Server cannot influence key →
Collision attack impossible
Key Differences:
| Aspect | TLS 1.2 | TLS 1.3 |
|——–|———|———|
| Session ID | Server-selected | N/A (tickets used) |
| Cache key | Server-controlled | Client-chosen |
| Encryption | Optional | Mandatory |
| Collision risk | High | None |
Client-Chosen Cache Keys
TLS 1.3 implementations use client-chosen cache keys derived from:
- Ticket content (encrypted by server)
- Client-side random values
- Application-specific identifiers
An attacker cannot predict or influence these keys, making cache collision attacks impossible.
Ticket-Based Resumption Safety
When TLS 1.2 uses ticket-based resumption with an empty ServerHello session ID:
ServerHello {
session_id = ""; // Empty
session_ticket = <encrypted_ticket>;
}
The client chooses the cache key, not the server. This variant is safe even in TLS 1.2.
Safe TLS 1.2 Configuration:
SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); // Disable tickets
// Forces use of server-selected Session IDs (vulnerable)
// OR
SSL_CTX_set_options(ctx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
// Limits resumption scope
Implementation Details for Security Teams
Affected Functions and Code Paths
Primary Vulnerable Functions:
ClientSessionToSession()
- Location:
src/ssl.c - Purpose: Dereferences session reference handles
- Vulnerability: Validates only session ID hash, not peer identity
- Fix: Added generation counter validation
AddSessionToCache()
- Location:
src/ssl.c - Purpose: Writes session data to process-global cache
- Vulnerability: No validation of peer identity or context
- Fix: Added per-write generation counter
wolfSSL_get_session()/SSL_get_session()
- Location:
src/ssl.c - Purpose: Returns session reference handle
- Vulnerability: Returns reference instead of copy
- Mitigation: Use
wolfSSL_get1_session()instead
wolfSSL_set_session()/SSL_set_session()
- Location:
src/ssl.c - Purpose: Applies cached session to new connection
- Vulnerability: Uses handle without context validation
- Fix: Added generation counter verification
Cache Version Indicators
Version 2 (Vulnerable):
#define WOLFSSL_CACHE_VERSION 2
typedef struct {
byte sessionID[ID_LEN];
byte masterSecret[SECRET_LEN];
byte cipherSuite[2];
// No generation counter
} SessionCache_v2;
Version 3 (Patched):
#define WOLFSSL_CACHE_VERSION 3
typedef struct {
byte sessionID[ID_LEN];
byte masterSecret[SECRET_LEN];
byte cipherSuite[2];
word32 generation; // Per-write generation counter
} SessionCache_v3;
Session ID Collision Mechanics
Collision Calculation:
// Hash function for session ID
word32 SessionIDHash(const byte *sessionID, word32 len) {
word32 hash = 0;
for (word32 i = 0; i < len; i++) {
hash = ((hash << 5) + hash) + sessionID[i];
}
return hash % SESSION_CACHE_SIZE;
}
// Vulnerable lookup
ClientSession *FindSession(const byte *sessionID, word32 len) {
word32 hash = SessionIDHash(sessionID, len);
// Returns entry at hash index WITHOUT peer verification
return &sessionCache[hash];
}
Collision Exploitation:
An attacker crafts Session IDs that hash to the same cache index:
Legitimate Server A → Session ID: 0x12345678 → hash = 42
Attacker Server B → Session ID: 0x87654321 → hash = 42 (collision!)
↓
Overwrites cache[42]
Validation Bypass Mechanisms
Abbreviated Handshake Flow:
Client Server
| |
|--- ClientHello (with SID) --->|
| |
|<-- ServerHello (same SID) ----|
| |
|--- [ChangeCipherSpec] ------->|
|--- Finished ----------------->|
| |
|<-- [ChangeCipherSpec] --------|
|<-- Finished ------------------|
| |
| NO Certificate message! |
| NO domain validation! |
| NO X.509 verification! |
Validation Bypass Code Path:
// Abbreviated handshake processing
if (session_id_matches_cache) {
// Load cached session
LoadCachedSession(ssl, cached_session);
// Skip certificate processing
// wolfSSL_check_domain_name() NOT called
// X509_verify_cert() NOT called
// Complete handshake with cached master secret
CompleteAbbreviatedHandshake(ssl);
}
Frequently Asked Questions
Q: Is TLS 1.3 affected by CVE-2026-94419?
A: No. TLS 1.3 uses client-chosen cache keys and is structurally immune to server-side session ID collision attacks. The protocol’s ticket-based resumption mechanism prevents attackers from predicting or influencing cache keys.
Q: Are popular integration builds like --enable-curl or --enable-nginx vulnerable?
A: No. Fifteen standard integration configurations explicitly set NO_SESSION_CACHE_REF and are protected by default. These include --enable-curl, --enable-nginx, --enable-haproxy, --enable-stunnel, --enable-wpas, and --enable-distro.
Q: Why does domain verification fail during exploitation?
A: Abbreviated TLS handshakes omit the Certificate message exchange per protocol specification. Because wolfSSL receives no certificate, it cannot perform domain name checks (wolfSSL_check_domain_name()) or X.509 validation. The client accepts the connection as validated based solely on the cached master secret.
Q: How long does a poisoned cache entry persist?
A: By default, poisoned entries remain active for 500 seconds (the default session timeout) or until displaced by normal cache eviction. This creates a practical exploitation window for multiple attack attempts.
Q: What is the official vendor fix?
A: The fix introduces per-write generation counters to the session cache and increments WOLFSSL_CACHE_VERSION from 2 to 3. This forces rejection of legacy cache formats and prevents poisoned entries from being used by patched builds.
Q: Can I use wolfSSL_get1_session() as a workaround?
A: Yes. Replacing wolfSSL_get_session() with wolfSSL_get1_session() returns isolated session copies instead of references, preventing cache manipulation. This provides immediate protection without recompilation.
Q: Which build flags disable the vulnerable cache entirely?
A: Use --enable-leantls, --enable-leanpsk, --enable-lowresource, or --enable-tinytls13 to disable client session caching completely. These options eliminate the vulnerability by removing the vulnerable code path.
Q: Does this affect DTLS 1.3?
A: No. Only TLS 1.2 and DTLS 1.2 are vulnerable due to server-selected session IDs in those protocol versions. DTLS 1.3 uses the same ticket-based resumption as TLS 1.3 and is unaffected.
Q: How can I verify my build is patched?
A: Check the wolfSSL version (wolfssl-config --version should show v5.9.3+) and verify the cache version indicator (strings libwolfssl.so | grep WOLFSSL_CACHE_VERSION should show 3).
References
- CVE Identifier: CVE-2026-94419
- Vendor: wolfSSL Inc. (https://www.wolfssl.com)
- Affected Library: wolfSSL Embedded SSL/TLS Library
- Affected Versions: v5.3.0 through v5.9.2
- Fixed Versions: v5.9.3 and later
- Affected Functions:
wolfSSL_get_session(),SSL_get_session(),ClientSessionToSession(),AddSessionToCache() - Related Standards: RFC 5246 (TLS 1.2), RFC 6347 (DTLS 1.2), RFC 8446 (TLS 1.3)
Document Classification: Security Advisory
Last Updated: 2024
Recommended Review: Quarterly or upon vendor updates