✓ProveIt
Examples / Report #3516202 Saved analysis

Cookie Replacement Use-After-Free Vulnerability

curl · 2026-01-1910.8s analysis · $0.016 in model calls

What the report claims

The cookie replacement logic in lib/cookie.c contains a use-after-free vulnerability in the replace_existing() function due to modifying a linked list while iterating over it.

Compare with curl's recorded outcome

Labeled AI slop

curl's maintainer lists this report as AI slop

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 code at lib/cookie.c:915 accesses repl->creationtime BEFORE freecookie(repl, TRUE) at line 921 - this is use-before-free, not use-after-free
  • The linked list iteration (lines 833-910) completes fully before any node removal (line 918) or free (line 921) occurs - no modification during iteration
  • The report's central claim of use-after-free is directly contradicted by the provided source code
How this analysis ran 5 claims · 7 citations verified

Checked against curl master on 2026-01-19 (9703dabd77b5). Dev 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 · 10 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2527 tokens · $0.0003
3 · Evidence
6 code snippets
real curl source at 9703dabd77b5 · 6 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
11843 tokens · 7.8s · $0.0160
5 · Receipts
7 of 9 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the report. However, the code in lib/cookie.c shows that replace_existing() accesses the old cookie's creationtime (line 915) BEFORE calling freecookie() (line 921), and the list iteration completes entirely before any modification. This is not a use-after-free. The PoC file was not provided for verification. Could you clarify if you're seeing a different code path or version?

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

Report claims

C1flaw contradicted

replace_existing() modifies a linked list while iterating over it, creating potential for memory corruption

Read the reasoning

The report claims a use-after-free where 'co->creationtime = repl->creationtime' accesses freed memory. However, the code shows the access at line 915 occurs before freecookie() at line 921. The linked list iteration (lines 833-910) completes entirely before any modification (lines 911-922). No use-after-free exists in the provided code.

C2location supported

lib/cookie.c:822-925

Read the reasoning

The location claim is accurate - the function spans lines 822-925 in the provided source.

C3trigger not verifiable

race condition server triggers the vulnerability

Read the reasoning

The PoC server code (poc_cookie_uaf.py) was not provided in the source material, so the trigger claim cannot be verified against the codebase.

poc_cookie_uaf.py:0
C4impact contradicted

potential for arbitrary code execution

Read the reasoning

Since the use-after-free claim (C1) is contradicted by the code, the claimed impact of arbitrary code execution from that flaw is also unsupported.

C5reproduction not verifiable

run curl with AddressSanitizer against exploit1, exploit2, or exploit3 endpoints

Read the reasoning

The reproduction steps reference a PoC file and exploit endpoints that were not provided in the source material.

poc_cookie_uaf.py:0

What the report says about other projects

not addressed AddressSanitizer is required to detect the vulnerability reliably

not addressed The vulnerability is classified as a use-after-free error (CWE-416)

Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: The cookie replacement logic in `lib/cookie.c` contains a use-after-free vulnerability in the `replace_existing()` function. The function modifies a linked list while iterating over it, creating potential for memory corruption in concurrent or complex cookie operations. **Vulnerable Code Location:** `lib/cookie.c:822-925` ```c static bool replace_existing(struct Curl_easy *data, struct Cookie *co, struct CookieInfo *ci, bool secure, bool *replacep) { bool replace_old = FALSE; struct Curl_llist_node *replace_n = NULL; struct Curl_llist_node *n; // Iterate over cookie list for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) { struct Cookie *clist = Curl_node_elem(n); // ... comparison logic ... if(replace_old) replace_n = n; // Store node for later deletion } // Delete node after iteration if(replace_n) { struct Cookie *repl = Curl_node_elem(replace_n); co->creationtime = repl->creationtime; // Access freed memory Curl_node_remove(replace_n); freecookie(repl, TRUE); // Free memory } } ``` ## Affected version curl 7.x - 8.x ## Steps To Reproduce: ### PoC Server A race condition server has been developed to trigger the vulnerability: **File:** `poc_cookie_uaf.py` ```bash # Run the PoC server python3 poc_cookie_uaf.py # Server starts on http://localhost:8000 # Provides 3 different exploit scenarios ``` ### Testing with AddressSanitizer **CRITICAL:** This vulnerability requires AddressSanitizer to detect reliably. #### Build curl with ASan ```bash cd curl export CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" export LDFLAGS="-fsanitize=address" ./buildconf ./configure --enable-debug --disable-shared make clean make ``` #### Test 1: Rapid Cookie Replacement ```bash # Single request with 50 cookies ./src/curl -c cookies.txt http://localhost:8000/exploit1 # Check for ASan errors # Look for: "heap-use-after-free" ``` **Expected ASan Output (if vulnerable):** ``` ==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000001234 READ of size 8 at 0x602000001234 thread T0 #0 0x... in replace_existing lib/cookie.c:915 #1 0x... in Curl_cookie_add lib/cookie.c:1234 …

Source code

lib/cookie.clines 797–950 · lines 822-925 cited by the report
C2: replace_existing function starts at line 822
C1: The if(replace_n) block starts after the for loop completes at line 910
C1: co->creationtime = repl->creationtime; accesses repl BEFORE freecookie is called
C4: Access to repl->creationtime happens before free
C1: freecookie(repl, TRUE); is called AFTER the creationtime copy, not before
C4: freecookie called after access
C2: replace_existing function ends at line 925
lib/llist.clines 207–212 · definition of Curl_llist_head
207struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list)
208{
209 DEBUGASSERT(list);
210 DEBUGASSERT(list->_init == LLISTINIT);
211 return VERIFYNODE(list->_head);
212}
lib/llist.clines 234–239 · definition of Curl_node_elem
Show 6 lines (not cited by the judge)
234void *Curl_node_elem(struct Curl_llist_node *n)
235{
236 DEBUGASSERT(n);
237 DEBUGASSERT(n->_init == NODEINIT);
238 return n->_ptr;
239}
lib/llist.clines 243–248 · definition of Curl_node_next
Show 6 lines (not cited by the judge)
243struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n)
244{
245 DEBUGASSERT(n);
246 DEBUGASSERT(n->_init == NODEINIT);
247 return VERIFYNODE(n->_next);
248}
lib/llist.clines 191–194 · definition of Curl_node_remove
Show 4 lines (not cited by the judge)
191void Curl_node_remove(struct Curl_llist_node *e)
192{
193 Curl_node_uremove(e, NULL);
194}
lib/cookie.clines 64–72 · definition of freecookie
Show 9 lines (not cited by the judge)
64static void freecookie(struct Cookie *co, bool maintoo)
65{
66 curlx_free(co->domain);
67 curlx_free(co->path);
68 curlx_free(co->name);
69 curlx_free(co->value);
70 if(maintoo)
71 curlx_free(co);
72}