✓ProveIt
Examples / Report #2410774 Saved analysis

CVE-2024-2379: QUIC certificate check bypass with wolfSSL

curl · 2024-03-1024.4s analysis · $0.019 in model calls

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

Needs a policy review

The code does what the report says, but curl's own policy may exclude it. A maintainer decides.

curl policy: API misuse
Judgment withdrawn. The answer failed the evidence check.
See 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.

1 · Grounding
grounded
no model · 6 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2491 tokens · $0.0003
3 · Evidence
1 code snippets
real curl source at fcef00db1a19 · 5 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
10641 tokens · 13.0s · $0.0183
5 · Receipts
17 of 17 citations verified
every citation must point at code the judge was shown
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

C1flaw supported

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.

C2location supported

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.

C3trigger supported

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.

C4impact supported

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.

C5reproduction supported

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)

Full report on HackerOne ↗

## Summary: In `vquic-tls.c` `curl_wssl_init_ctx` errors are handled by `goto out` and having `result` be set to an error code to be returned. At the beginning of the function `result` is correctly set to `CURLE_FAILED_INIT` which allows for `goto out` to work correctly without having to set `result` however, `result`'s value is overridden at a certain point if `ctx_setup` is passed to the function. If `ctx_setup` returns 0 (the expected result) then it's assigned to `result` and any attempt after that to `goto out` without setting `result` to an error code will make the function skip the rest of its initialization and return with an error code indicating success. Unfortunately the last thing `curl_wssl_init_ctx` is supposed to setup for the ssl context is the certificate verification requirements. There are 4 places `goto out` is used without setting `result`, of those 3 can result from bad user input (bad tls13-ciphers, curves, or cafile/capath) and 1 is from trying to setup ssl key logging when having a WolfSSL build that doesn't have `wolfSSL_CTX_set_keylog_callback`. Luckily this does require the user to have passed in bogus values for one of the above parameters which I find very unlikely. Also very fortunately WolfSSL attempts to default to verify a cert rather than OpenSSL's default of not verifying. There is an option to make WolfSSL have OpenSSL compatible defaults but I don't know how common it is to have WolfSSL configured like that so I'm not sure how likely it is that people could run into this. Given the unlikely set of configurations required to encounter this I don't think this is a "high" vulnerability like the CVSS claims but there is no way of manually setting the score, honestly I would have just submitted a patch to fix this but I'm not to sure on how common having WolfSSL in OpenSSL compatible mode is so I'm err'ing on the side of caution and submitting it here. I checked the other initialization functions in `vquic-tls.c` and it doesn't look like the same mistake was made in them. `result` is assigned before each use of `goto out`. ## Steps To Reproduce: Build WolfSSL with something that sets `OPENSSL_COMPATIBLE_DEFAULTS` (I used `--enable-nginx`) and build curl with the WolfSSL backend. Setup a QUIC webserver with a self signed cert that matches the domain being spoofed and attempt to make a HTTP/3 connection to it using curl …

Source code

lib/vquic/vquic-tls.clines 344–453 · definition of curl_wssl_init_ctx
344static 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
447out:
448 if(result && ctx->ssl_ctx) {
449 SSL_CTX_free(ctx->ssl_ctx);
450 ctx->ssl_ctx = NULL;
451 }
452 return result;
453}