IT Security 1 month ago

CVE-2026-94419 WolfSSL Session Cache Poisoning Vulnerability – Analysis & Mitigation

CVE-2026-94419 is a critical authentication bypass in wolfSSL’s TLS 1.2/DTLS 1.2 session cache that allows attackers to poison the global cache, bypass certificate validation, and impersonate servers. This article details the flaw, attack vectors, detection, and remediation. CVE-2026-94419 is a critical authentication bypass in wolfSSL’s TLS 1.2/DTLS 1.2 session cache that allows attackers to poison the global cache, bypass certificate validation, and impersonate servers. This…

IT Security

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() or SSL_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_REF
  • NO_SESSION_CACHE
  • NO_CLIENT_CACHE
  • TITAN_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:

  1. 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
  2. Triggering the write path: AddSessionToCache() processes the incoming session data
  3. Bypassing validation: The write path performs no checks on:
  • Peer identity verification
  • Server ID comparison
  • WOLFSSL_CTX isolation
  1. 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.

Cache structure showing how session ID collision enables credential overwrite across context boundaries
Cache structure showing how session ID collision enables credential overwrite across context boundaries

Authentication Bypass During Abbreviated Handshakes

When the application later initiates a connection to the legitimate target server using the poisoned handle:

  1. Abbreviated handshake initiation: wolfSSL recognizes the cached session and sends an abbreviated handshake request
  2. Certificate omission: Per TLS 1.2 specification, abbreviated handshakes omit the Certificate message
  3. 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
  1. 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:

  1. Initial compromise: Attacker gains network position (compromised router, BGP hijacking, DNS poisoning) or controls a legitimate but untrusted endpoint
  2. 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
  3. Cache overwrite: AddSessionToCache() overwrites the legitimate API server’s cached master secret with the attacker’s credentials
  4. Resumption attack: Application initiates connection to api.internal.company.com using its cached session handle
  5. 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.
  6. 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:

  1. Identical Session IDs across different targets:
   Connection A → Server X → Session ID: 0xABCD1234
   Connection B → Server Y → Session ID: 0xABCD1234  ← Suspicious
  1. 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
  1. 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:

  1. Inventory Phase:
   grep -r "SSL_get_session\|wolfSSL_get_session" . > session_usage.txt
   # Document all occurrences
  1. Analysis Phase:
  • Identify session storage patterns
  • Determine session lifetime requirements
  • Map session usage across contexts
  1. 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
  1. Testing Phase:
  • Unit tests for session resumption
  • Integration tests across contexts
  • Load testing for memory leaks
  • Security testing for cache isolation
  1. 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:

  1. ClientSessionToSession()
  • Location: src/ssl.c
  • Purpose: Dereferences session reference handles
  • Vulnerability: Validates only session ID hash, not peer identity
  • Fix: Added generation counter validation
  1. 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
  1. 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
  1. 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

← Back to IT Security