HexBrain

curl · Report #3973090

CVE-2026-82208: wolfSSL CA-cache hit overrides callback

Validstatus
3upvotes
3 Sept 2026disclosed

How it was found

Summary

With the wolfSSL backend, when CA caching is enabled and an CURLOPT_SSL_CTX_FUNCTION callback replaces the trust store, curl can silently reinstall the cached store after the callback returns. A certificate trusted by the cached store but rejected by the callback-selected store is then incorrectly accepted.

Root cause

Curl_wssl_setup_x509_store() in lib/vtls/wolfssl.c installs a cached X509 store but does not set wssl->x509_store_setup = TRUE. That flag is only set inside wssl_populate_x509_store(), which runs on cache misses.

The sequence on a cache hit:

  1. Before invoking the callback, wssl_connect_step1() calls Curl_wssl_setup_x509_store() — cached store A is installed, flag stays false.
  2. The CURLOPT_SSL_CTX_FUNCTION callback runs and replaces store A with store B.
  3. During the handshake, the still-false flag causes Curl_wssl_setup_x509_store() to run again. It finds CTX_get_cert_store() != cached_store (the callback changed it), so it reinstalls cached store A.

Affected versions

Introduced in commit 0f2876b2c33f (Jul 2024, "wolfSSL: CA store share fix" for #14278). Present in releases 8.9.1 through 8.21.0.

Impact

An application that uses CURLOPT_SSL_CTX_FUNCTION to restrict trust (e.g. pin to a private CA) has that restriction silently bypassed when CA caching is active. A MITM presenting a certificate valid under the cached store but rejected by the callback's store succeeds.

Prerequisites: wolfSSL backend, CURLOPT_CA_CACHE_TIMEOUT != 0, a callback that replaces the store, and differing trust between the two stores.

Reproduction

  1. Build an affected curl release (8.9.1 through 8.21.0) against wolfSSL and configure curl without a compiled-in default CA path so that a CA file is eligible for caching.
  2. Create two independent certificate authorities, called CA A and CA B.
  3. Create a TLS server certificate for the test hostname, including a matching subject alternative name, and sign it with CA A.
  4. Start an HTTPS server on the test hostname using that certificate.
  5. Write a small libcurl client that uses the same multi handle for two transfers, sets CURLOPT_CAINFO to CA A, and sets a nonzero CURLOPT_CA_CACHE_TIMEOUT.
  6. Make the first transfer without an SSL-context callback. Confirm that it succeeds and thereby populates curl's CA cache with the trust store containing CA A.
  7. For the second transfer, use a fresh connection and register CURLOPT_SSL_CTX_FUNCTION. In the callback, replace the wolfSSL context's certificate store with a newly created store containing only CA B.
  8. Disable connection reuse and TLS session caching for both transfers so that the second transfer performs a new TLS handshake and certificate verification.
  9. Make the second transfer to the same HTTPS server. On a vulnerable build, it incorrectly succeeds even though the callback-selected store contains only CA B and the server certificate chains only to CA A.
  10. Repeat the sequence with CURLOPT_CA_CACHE_TIMEOUT set to zero. The second transfer should now fail certificate verification, demonstrating that the unexpected acceptance depends on the CA-cache path.
  11. As a fixed-version check, repeat the cached sequence on a build containing commit ed0338befd or a later release. The second transfer should fail because curl no longer reinstalls CA A after the callback selects CA B.

The callback-selected trust store must remain authoritative. The transfer using store B should reject the server certificate regardless of whether curl's CA cache was previously populated.

Suggested fix

Set wssl->x509_store_setup = TRUE in Curl_wssl_setup_x509_store() itself (before returning), rather than only inside wssl_populate_x509_store(). This ensures the flag is set on both cache hits and misses, preventing the second invocation from overwriting the callback's store.

Impact

Summary:

An attacker able to present a hostname-valid certificate trusted by curl’s cached CA store—but rejected by the application-selected store—can impersonate the intended TLS endpoint. This bypasses the application’s trust policy and may allow interception or modification of encrypted traffic.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original