✓ProveIt
Examples / Report #2148242 Saved analysis

CVE-2023-38546: cookie injection with none file

curl · 2023-09-1415.3s analysis · $0.017 in model calls

What the report claims

A cookie injection vulnerability exists when duplicating a curl easy handle if a file named 'none' exists in the current working directory, allowing cookies to be injected into the duplicated handle.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2023-38546 cookie injection with none file

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Code contradicts the claim

The code provides evidence against the report's central claim. Review the cited lines before deciding.

Why this finding?
  • The behavior described is the intended cookie persistence feature: curl_easy_duphandle() duplicates the parent handle's cookie configuration including its filename (lib/easy.c:925-934).
  • The default cookie filename 'none' is a documented design choice (lib/cookie.c:31). Applications that enable cookies without setting a custom filename will use 'none'; this is not a vulnerability.
  • Loading cookies from the configured cookie file and sending them with requests is the documented purpose of libcurl's cookie support, not 'injection'.
  • Exploitation requires the attacker to already have write access to the application's current working directory and the application to have explicitly enabled cookies with the default filename — a configuration issue, not a curl vulnerability.
How this analysis ran 5 claims · 5 citations verified

Checked against curl master on 2023-09-14 (267e14f1bab6). 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 · 1390 tokens · $0.0002
3 · Evidence
2 code snippets
real curl source at 267e14f1bab6
4 · Judgment
Nemotron-3-Ultra-550b-a55b
9366 tokens · 13.2s · $0.0170
5 · Receipts
5 of 5 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

This report describes intended behavior, not a vulnerability. curl_easy_duphandle() duplicates the parent handle's cookie configuration including its filename (lib/easy.c:925-934). The default cookie filename is 'none' (lib/cookie.c:31), so if the application enables cookies without setting CURLOPT_COOKIEFILE, both handles will load cookies from a file named 'none' in the cwd. This is the documented cookie persistence feature. Applications that do not want this should set an explicit cookie filename or disable cookies. An attacker who can write files to the application's cwd already has significant control; this does not constitute a security flaw in curl.

Inspect all claims and source code 5 claims · 2 code excerpts

Report claims

C1flaw contradicted

A file named 'none' in the current working directory causes cookies to be injected into a duplicated handle during curl_easy_duphandle()

Read the reasoning

The report frames loading cookies from the configured cookie file as 'injection'. In reality, curl_easy_duphandle() duplicates the parent handle's cookie configuration including its filename (line 929). If the parent handle uses the default filename 'none', the duplicate will also use 'none' and load cookies from that file. This is the intended cookie persistence feature, not a vulnerability. The default filename 'none' is a design choice; applications that enable cookies should set an explicit filename via CURLOPT_COOKIEFILE if they do not want to use the default.

C2location contradicted

The flaw occurs in curl_easy_duphandle() when copying cookies from the parent handle to the duplicated handle

Read the reasoning

The code at lines 925-934 explicitly documents this as intended behavior: 'If cookies are enabled in the parent handle, we enable them in the clone as well!' Duplicating the cookie configuration (including the filename) is the correct semantics for a handle duplication function.

C3trigger contradicted

The presence of a file named 'none' in the current working directory triggers the injection

Read the reasoning

The presence of a file named 'none' triggers cookie loading only because 'none' is the default cookie filename when the application enables cookies without specifying a custom filename. This is configuration-dependent behavior, not an unconditional trigger. An application that sets CURLOPT_COOKIEFILE to a different path would not be affected.

C4impact contradicted

Cookies can be injected into an HTTP request via the duplicated handle

Read the reasoning

Sending cookies from the cookie jar with HTTP requests is the documented purpose of libcurl's cookie support. The duplicated handle inherits the parent's cookie configuration, so it will send the same cookies. This is not 'injection' — it is the feature working as designed.

C5reproduction not verifiable

The proof of concept uses a file named 'none' and a compiled poc_cookieinj program to inject cookies

Read the reasoning

The PoC is not provided in the source code. The deterministic checks only confirm the quoted lines exist in the repository. Whether the PoC demonstrates a vulnerability or merely the intended cookie-loading behavior cannot be verified from the provided code alone.

Read the original report

Full report on HackerOne ↗

When `curl_easy_duphandle()` is used to duplicate an easy handle it is possible to inject cookies into that duplicated handle if a file `none` exists in the current working directory. ## PoC / Steps to reproduce: 1. Open 2 terminals 1. compile F2699218 1. in terminal 1: `nc -l -p 8888 127.0.0.1` 1. in terminal 2: * `echo -e "127.0.0.1\tTRUE\t/\tFALSE\t0\tname\tvalue" > none` * `/path/to/poc_cookieinj` (`poc_cookieinj` does not have to be in the same directory as the file `none`) ## Supporting Material/References: The flaw is in `Curl_cookie_init()` ``` if(!inc) { /* we didn't get a struct, create one */ c = calloc(1, sizeof(struct CookieInfo)); if(!c) return NULL; /* failed to get memory */ c->filename = strdup(file?file:"none"); /* copy the name just in case */ if(!c->filename) goto fail; /* failed to get memory */ ``` and in `curl_easy_duphandle()` the injection happens ``` if(data->cookies) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ outcurl->cookies = Curl_cookie_init(data, data->cookies->filename, outcurl->cookies, data->set.cookiesession); if(!outcurl->cookies) goto fail; } ``` ## Impact cookies injection into an http request

Source code

lib/cookie.clines 31–124 · definition of Curl_cookie_init
C1: Curl_cookie_init declaration shows it takes a file parameter; the quoted implementation uses file?file:"none" as the default when no filename is provided
C3: The default filename 'none' is used only when no explicit filename is provided to Curl_cookie_init
lib/easy.clines 896–1038 · definition of curl_easy_duphandle
896struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
897{
898 struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy));
899 if(!outcurl)
900 goto fail;
901
902 /*
903 * We setup a few buffers we need. We should probably make them
904 * get setup on-demand in the code, as that would probably decrease
905 * the likeliness of us forgetting to init a buffer here in the future.
906 */
907 outcurl->set.buffer_size = data->set.buffer_size;
908
909 /* copy all userdefined values */
910 if(dupset(outcurl, data))
911 goto fail;
912
913 Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
914
915 /* the connection cache is setup on demand */
916 outcurl->state.conn_cache = NULL;
917 outcurl->state.lastconnect_id = -1;
918 outcurl->state.recent_conn_id = -1;
919 outcurl->id = -1;
920
921 outcurl->progress.flags = data->progress.flags;
922 outcurl->progress.callback = data->progress.callback;
923
924#ifndef CURL_DISABLE_COOKIES
925 if(data->cookies) {
C2: The code block at lines 925-934 shows curl_easy_duphandle enabling cookies in the clone when the parent has cookies enabled — this is documented as intended behavior ('we enable them in the clone as well!')
926 /* If cookies are enabled in the parent handle, we enable them
927 in the clone as well! */
928 outcurl->cookies = Curl_cookie_init(data,
C1: curl_easy_duphandle passes data->cookies->filename to Curl_cookie_init, which is the parent handle's configured cookie filename
C4: Cookies from the configured file are loaded into the duplicated handle as part of normal cookie support
929 data->cookies->filename,
930 outcurl->cookies,
931 data->set.cookiesession);
932 if(!outcurl->cookies)
933 goto fail;
934 }
935
936 if(data->set.cookielist) {
937 outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist);
938 if(!outcurl->set.cookielist)
939 goto fail;
940 }
941#endif
942
943 if(data->state.url) {
944 outcurl->state.url = strdup(data->state.url);
945 if(!outcurl->state.url)
946 goto fail;
947 outcurl->state.url_alloc = TRUE;
948 }
949
950 if(data->state.referer) {
951 outcurl->state.referer = strdup(data->state.referer);
952 if(!outcurl->state.referer)
953 goto fail;
954 outcurl->state.referer_alloc = TRUE;
955 }
956
957 /* Reinitialize an SSL engine for the new handle
958 * note: the engine name has already been copied by dupset */
959 if(outcurl->set.str[STRING_SSL_ENGINE]) {
960 if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
961 goto fail;
962 }
963
964#ifndef CURL_DISABLE_ALTSVC
965 if(data->asi) {
966 outcurl->asi = Curl_altsvc_init();
967 if(!outcurl->asi)
968 goto fail;
969 if(outcurl->set.str[STRING_ALTSVC])
970 (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
971 }
972#endif
973#ifndef CURL_DISABLE_HSTS
974 if(data->hsts) {
975 outcurl->hsts = Curl_hsts_init();
976 if(!outcurl->hsts)
977 goto fail;
978 if(outcurl->set.str[STRING_HSTS])
979 (void)Curl_hsts_loadfile(outcurl,
980 outcurl->hsts, outcurl->set.str[STRING_HSTS]);
981 (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
982 }
983#endif
984 /* Clone the resolver handle, if present, for the new handle */
985 if(Curl_resolver_duphandle(outcurl,
986 &outcurl->state.async.resolver,
987 data->state.async.resolver))
988 goto fail;
989
990#ifdef USE_ARES
991 {
992 CURLcode rc;
993
994 rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
995 if(rc && rc != CURLE_NOT_BUILT_IN)
996 goto fail;
997
998 rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
999 if(rc && rc != CURLE_NOT_BUILT_IN)
1000 goto fail;
1001
1002 rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
1003 if(rc && rc != CURLE_NOT_BUILT_IN)
1004 goto fail;
1005
1006 rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
1007 if(rc && rc != CURLE_NOT_BUILT_IN)
1008 goto fail;
1009 }
1010#endif /* USE_ARES */
1011
1012 Curl_initinfo(outcurl);
1013
1014 outcurl->magic = CURLEASY_MAGIC_NUMBER;
1015
1016 /* we reach this point and thus we are OK */
1017
1018 return outcurl;
1019
1020fail:
1021
1022 if(outcurl) {
1023#ifndef CURL_DISABLE_COOKIES
1024 curl_slist_free_all(outcurl->set.cookielist);
1025 outcurl->set.cookielist = NULL;
1026#endif
1027 Curl_safefree(outcurl->state.buffer);
1028 Curl_dyn_free(&outcurl->state.headerb);
1029 Curl_safefree(outcurl->state.url);
1030 Curl_safefree(outcurl->state.referer);
1031 Curl_altsvc_cleanup(&outcurl->asi);
1032 Curl_hsts_cleanup(&outcurl->hsts);
1033 Curl_freeset(outcurl);
1034 free(outcurl);
1035 }
1036
1037 return NULL;
1038}