CVE-2024-2379: QUIC certificate check bypass with wolfSSL
What the report claims
A flaw in curl's QUIC TLS initialization with WolfSSL allows bypass of certificate verification under specific configuration conditions, potentially enabling MITM attacks.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2024-2379 QUIC certificate check bypass with wolfSSL
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The code does what the report says, but curl's own policy may exclude it. A maintainer decides.
curl policy: API misuseSee the withdrawn model reasoning
The following reasoning failed the citation check and does not support the final finding.
- The code confirms the bug: ctx_setup success sets result=0, and subsequent error paths don't reset it, causing skipped certificate verification and false success return.
- However, the bug only triggers when the user passes invalid arguments (--curves, --tls13-ciphers, --cafile, --capath), which constitutes API misuse per project policy.
- The vulnerability also requires WolfSSL to be built with OPENSSL_COMPATIBLE_DEFAULTS (non-default), making the default verification behavior insecure.
- An attacker who can control the WolfSSL build configuration could disable verification directly; an attacker who can trick a user into passing bad arguments could trick them into using --insecure instead.
- The reporter themselves assesses the conditions as 'unlikely' and not a 'high' vulnerability.
- Per curl's security policy, issues triggered only by API misuse (undocumented/unsupported usage) are not considered security problems.
How this analysis ran 5 claims · 17 citations verified
Evidence check: the behavior is real; the project's policy section 'API misuse' may exclude it
Checked against curl master on 2024-03-10 (fcef00db1a19). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Inspect the withdrawn draft reply
This draft belongs to the withdrawn judgment. It may contain unsupported claims.
Thank you for the detailed report. The code confirms the error-handling bug in curl_wssl_init_ctx: when ctx_setup succeeds, result becomes 0 and later error paths don't reset it, skipping certificate verification. However, this only triggers when a user passes invalid arguments (bad curves, ciphers, CAfile, etc.), which the project classifies as API misuse — not a security issue. Additionally, it requires WolfSSL built with OPENSSL_COMPATIBLE_DEFAULTS (non-default). An attacker who controls the build can disable verification directly; one who tricks a user into bad arguments can trick them into --insecure. Per our policy, this is not considered a security vulnerability.
Inspect all claims and source code 5 claims · 1 code excerpts
This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.
Report claims
result is not set before goto out in curl_wssl_init_ctx when ctx_setup succeeds, causing skipped initialization and incorrect error handling
Read the reasoning
The code confirms the flaw: when ctx_setup succeeds (returns 0), result is set to 0. Subsequent error paths (lines 378, 385, 395, 417) use goto out without resetting result to an error code, causing the function to return CURLE_OK (0) despite initialization failures. Certificate verification setup (lines 399-432) is skipped entirely.
in curl_wssl_init_ctx function in vquic-tls.c
Read the reasoning
The function curl_wssl_init_ctx is defined in vquic-tls.c starting at line 344 as shown in the provided source.
passing bad values for --curves, --tls13-ciphers, --tlscurves, or --cafile/capath arguments
Read the reasoning
The code shows these configuration values come from conn_config which is populated from user-provided options. Invalid values cause the wolfSSL calls to fail, triggering the problematic goto out paths.
certificate verification is bypassed, allowing potential MITM attacks
Read the reasoning
If an early error path is taken (bad curves/ciphers/keylog), the function jumps to out before reaching the certificate verification setup (lines 399-432). With WolfSSL built with OPENSSL_COMPATIBLE_DEFAULTS, the default is SSL_VERIFY_NONE, so verification is bypassed.
build WolfSSL with OPENSSL_COMPATIBLE_DEFAULTS, build curl with WolfSSL backend, and connect to a QUIC server with a self-signed cert using a bad --curves list
Read the reasoning
The reproduction steps are consistent with the code: a bad --curves value causes wolfSSL_CTX_set1_groups_list to fail (line 383), triggering goto out at line 385. If ctx_setup was called and succeeded, result remains 0, verification setup is skipped, and the function returns success.
What the report says about other projects
not addressed The flaw was introduced in pull request https://github.com/curl/curl/pull/12678
not addressed The only vulnerable version to be released is 8.6.0
Read the original report (excerpt)
Source code
| 344 | static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx, |
| C2: curl_wssl_init_ctx function definition at line 344 | |
| 345 | struct Curl_cfilter *cf, |
| 346 | struct Curl_easy *data, |
| 347 | Curl_vquic_tls_ctx_setup *ctx_setup) |
| 348 | { |
| 349 | struct ssl_primary_config *conn_config; |
| 350 | CURLcode result = CURLE_FAILED_INIT; |
| C1: result initialized to CURLE_FAILED_INIT | |
| 351 | |
| 352 | conn_config = Curl_ssl_cf_get_primary_config(cf); |
| 353 | if(!conn_config) { |
| 354 | result = CURLE_FAILED_INIT; |
| 355 | goto out; |
| 356 | } |
| 357 | |
| 358 | ctx->ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); |
| 359 | if(!ctx->ssl_ctx) { |
| 360 | result = CURLE_OUT_OF_MEMORY; |
| 361 | goto out; |
| 362 | } |
| 363 | |
| 364 | if(ctx_setup) { |
| C1: ctx_setup called and its return value assigned to result (line 365) | |
| C5: ctx_setup must be provided and succeed for result to be 0 | |
| 365 | result = ctx_setup(ctx, cf, data); |
| 366 | if(result) |
| C1: if result is non-zero, goto out; but if zero (success), result becomes 0 and execution continues | |
| 367 | goto out; |
| 368 | } |
| 369 | |
| 370 | wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx); |
| 371 | |
| 372 | if(wolfSSL_CTX_set_cipher_list(ctx->ssl_ctx, conn_config->cipher_list13 ? |
| C3: cipher_list13 from conn_config (user-controlled via --tls13-ciphers) | |
| 373 | conn_config->cipher_list13 : |
| 374 | QUIC_CIPHERS) != 1) { |
| 375 | char error_buffer[256]; |
| 376 | ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); |
| 377 | failf(data, "wolfSSL failed to set ciphers: %s", error_buffer); |
| 378 | goto out; |
| C1: goto out on cipher failure without setting result | |
| C4: early goto out skips all verification setup | |
| 379 | } |
| 380 | |
| 381 | if(wolfSSL_CTX_set1_groups_list(ctx->ssl_ctx, conn_config->curves ? |
| C3: curves from conn_config (user-controlled via --curves) | |
| C5: curves validation failure triggers goto out at line 385 | |
| 382 | conn_config->curves : |
| 383 | (char *)QUIC_GROUPS) != 1) { |
| 384 | failf(data, "wolfSSL failed to set curves"); |
| 385 | goto out; |
| C1: goto out on curves failure without setting result | |
| 386 | } |
| 387 | |
| 388 | /* Open the file if a TLS or QUIC backend has not done this before. */ |
| 389 | Curl_tls_keylog_open(); |
| 390 | if(Curl_tls_keylog_enabled()) { |
| 391 | #if defined(HAVE_SECRET_CALLBACK) |
| 392 | wolfSSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback); |
| 393 | #else |
| 394 | failf(data, "wolfSSL was built without keylog callback"); |
| 395 | goto out; |
| C1: goto out on keylog callback failure without setting result | |
| 396 | #endif |
| 397 | } |
| 398 | |
| 399 | if(conn_config->verifypeer) { |
| C4: certificate verification setup begins here (wolfSSL_CTX_set_verify) | |
| 400 | const char * const ssl_cafile = conn_config->CAfile; |
| C3: CAfile from conn_config (user-controlled via --cafile) | |
| 401 | const char * const ssl_capath = conn_config->CApath; |
| C3: CApath from conn_config (user-controlled via --capath) | |
| 402 | |
| 403 | wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); |
| 404 | if(ssl_cafile || ssl_capath) { |
| 405 | /* tell wolfSSL where to find CA certificates that are used to verify |
| 406 | the server's certificate. */ |
| 407 | int rc = |
| 408 | wolfSSL_CTX_load_verify_locations_ex(ctx->ssl_ctx, ssl_cafile, |
| 409 | ssl_capath, |
| 410 | WOLFSSL_LOAD_FLAG_IGNORE_ERR); |
| 411 | if(SSL_SUCCESS != rc) { |
| 412 | /* Fail if we insist on successfully verifying the server. */ |
| 413 | failf(data, "error setting certificate verify locations:" |
| 414 | " CAfile: %s CApath: %s", |
| 415 | ssl_cafile ? ssl_cafile : "none", |
| 416 | ssl_capath ? ssl_capath : "none"); |
| 417 | goto out; |
| C1: goto out on CAfile/CApath failure without setting result | |
| 418 | } |
| 419 | infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); |
| 420 | infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); |
| 421 | } |
| 422 | #ifdef CURL_CA_FALLBACK |
| 423 | else { |
| 424 | /* verifying the peer without any CA certificates won't work so |
| 425 | use wolfssl's built-in default as fallback */ |
| 426 | wolfSSL_CTX_set_default_verify_paths(ctx->ssl_ctx); |
| 427 | } |
| 428 | #endif |
| 429 | } |
| 430 | else { |
| 431 | wolfSSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_NONE, NULL); |
| C4: when verifypeer is false, SSL_VERIFY_NONE is set | |
| 432 | } |
| 433 | |
| 434 | /* give application a chance to interfere with SSL set up. */ |
| 435 | if(data->set.ssl.fsslctx) { |
| 436 | Curl_set_in_callback(data, true); |
| 437 | result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx, |
| 438 | data->set.ssl.fsslctxp); |
| 439 | Curl_set_in_callback(data, false); |
| 440 | if(result) { |
| 441 | failf(data, "error signaled by ssl ctx callback"); |
| 442 | goto out; |
| 443 | } |
| 444 | } |
| 445 | result = CURLE_OK; |
| 446 | |
| 447 | out: |
| 448 | if(result && ctx->ssl_ctx) { |
| 449 | SSL_CTX_free(ctx->ssl_ctx); |
| 450 | ctx->ssl_ctx = NULL; |
| 451 | } |
| 452 | return result; |
| 453 | } |