HexBrain

curl · Report #3735179

CURLOPT_PROXY_CAINFO_BLOB silently activates native CA store on Apple builds

Informativestatus
15upvotes
19 May 2026disclosed

How it was found

Hi all,

CURLOPT_PROXY_CAINFO_BLOB (introduced 7.77.0) never sets proxy_ssl.custom_cablob. On USE_APPLE_SECTRUST / CURL_CA_NATIVE builds this causes curl to silently fall back to the system keychain for proxy TLS verification, nullifying the caller's blob-only trust policy.


Root cause

lib/setopt.c handles the two CA blob options back-to-back. The non-proxy handler sets the guard bit; the proxy handler never has:

/* CURLOPT_CAINFO_BLOB — correct */
if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) {
    s->ssl.custom_cablob = TRUE;          /* ← set */
    return Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob);
}

/* CURLOPT_PROXY_CAINFO_BLOB — bug */
if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
    return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob);
    /* proxy_ssl.custom_cablob never touched */

Curl_ssl_easy_config_complete() (lib/vtls/vtls.c:351) then sees three zero bits and arms the native store for the proxy config:

#if defined(USE_APPLE_SECTRUST) || defined(CURL_CA_NATIVE)
if(!sslc->custom_capath && !sslc->custom_cafile && !sslc->custom_cablob)
    sslc->native_ca_store = TRUE;   /* fires unconditionally on proxy blob path */
#endif

With native_ca_store set, ossl_apple_verify() (lib/vtls/openssl.c:4792) fires as a fallback whenever OpenSSL's in-process store (loaded from the blob) rejects the proxy certificate. Apple SecTrust then accepts any certificate signed by a system-trusted CA — including corporate MITM CAs installed via MDM — and the connection succeeds. Verbose output emits SSL certificate verified via Apple SecTrust. with no indication that blob-only trust was overridden.

Proof

Verified against an in-tree USE_APPLE_SECTRUST=ON build of 8.20.0. The test calls into the library directly — no network needed.

#include "curl_config.h"
#include "curl_setup.h"
#include <curl/curl.h>
#include "urldata.h"
#include "vtls/vtls.h"
#include <stdio.h>
#include <string.h>

int main(void)
{
  static const char ca[] = "-----BEGIN CERTIFICATE-----\nMIIB\n-----END CERTIFICATE-----\n";
  struct curl_blob blob = { (void *)ca, sizeof(ca), CURL_BLOB_COPY };
  struct Curl_easy *e;

  curl_global_init(CURL_GLOBAL_DEFAULT);
  e = (struct Curl_easy *)curl_easy_init();

  /* non-proxy: setopt sets custom_cablob, suppressing native store */
  curl_easy_setopt(e, CURLOPT_CAINFO_BLOB, &blob);
  Curl_ssl_easy_config_complete(e);
  printf("ssl.custom_cablob=%d ssl.native_ca_store=%d (expected 1 0)\n",
         e->set.ssl.custom_cablob, e->set.ssl.native_ca_store);

  /* proxy: setopt never sets custom_cablob; native store fires */
  curl_easy_setopt(e, CURLOPT_PROXY_CAINFO_BLOB, &blob);
  e->set.proxy_ssl.native_ca_store = 0;
  Curl_ssl_easy_config_complete(e);
  printf("proxy_ssl.custom_cablob=%d proxy_ssl.native_ca_store=%d (expected 1 0)\n",
         e->set.proxy_ssl.custom_cablob, e->set.proxy_ssl.native_ca_store);

  curl_easy_cleanup(e);
  curl_global_cleanup();
  return e->set.proxy_ssl.native_ca_store; /* 1 = bug */
}

Output (exit code 1):

$ proof_cablob
ssl.custom_cablob=1 ssl.native_ca_store=0 (expected 1 0)
proxy_ssl.custom_cablob=0 proxy_ssl.native_ca_store=1 (expected 1 0)

Fix

One line in lib/setopt.c, mirroring the non-proxy case:

-    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
-      return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob);
+    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) {
+      s->proxy_ssl.custom_cablob = TRUE;
+      return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob);
+    }

Thanks, AISLE Research Team

Impact

Setting CURLOPT_PROXY_CAINFO_BLOB on Apple builds silently falls back to trusting the system keychain for proxy TLS, allowing a proxy certificate signed by any system-trusted CA — including MDM-installed corporate MITM CAs — to be accepted even though the caller supplied an explicit CA blob expecting blob-only trust

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