# REPRO-2026-00347: curl: Secure cookie attribute bypass when a TAB character precedes the Secure attribute in Set-Cookie ## Summary Status: published Severity: low CVSS: Unknown CWE: CWE-201: Information Exposure Through Sent Data Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00347 CVE: CVE-2026-80255 ## Package Name: curl/curl Ecosystem: github Affected: curl 8.13.0 through 8.21.0 inclusive; the machine-readable advisory lists 8.13.0, 8.14.0, 8.14.1, 8.15.0, 8.16.0, 8.17.0, 8.18.0, 8.19.0, 8.20.0, and 8.21.0. Fixed: curl 8.22.0 ## Root Cause # Root Cause Analysis: CVE-2026-80255 ## Summary CVE-2026-80255 is a curl/libcurl HTTP cookie-parser vulnerability in which a response attribute encoded as `;\tSecure` is not recognized as the `Secure` attribute. In the tested vulnerable fix parent, curl persisted `sess=SECRET` with its secure-only field set to `FALSE`; a later invocation of the real curl command-line product then transmitted `Cookie: sess=SECRET` to a plaintext HTTP peer. The exact fixed commit persisted the same cookie with `Secure=TRUE` and omitted it from the equivalent plaintext request. ## Impact - **Package/component affected:** curl/libcurl, specifically `lib/cookie.c` HTTP `Set-Cookie` parsing as exercised by the curl command-line product. - **Affected versions:** The advisory reports curl 8.13.0 through 8.21.0, introduced by `1aea05a6c2699e80c75936d5`. Per the fixed-commit checkout rule, this run tested the exact fix parent `621e507300e6c83966567b6ae0352ca82544df13` as vulnerable and `4f6aa41a0145e930e766775dbe860883d350aa0a` as fixed. - **Risk level and consequences:** Low severity, but with a concrete confidentiality consequence. A server capable of returning the crafted `Set-Cookie` header can cause affected curl clients to disclose a cookie intended for encrypted transport in a later plaintext HTTP request to the same non-localhost origin. The cookie is visible to that HTTP peer and to network observers on the plaintext path. ## Impact Parity - **Disclosed/claimed maximum impact:** Information disclosure of a cookie intended to have the `Secure` transport restriction. - **Reproduced impact from this run:** The literal secret `sess=SECRET` was captured in plaintext HTTP request bytes in two independent vulnerable product attempts after being set through two independent HTTPS TCP sessions. - **Parity:** `full` - **Not demonstrated:** No higher impact such as arbitrary memory disclosure or code execution was claimed or attempted. The demonstrated disclosure is bounded to the affected application's cookie data and workflow. ## Root Cause The parser in the vulnerable commit scans each cookie attribute name with: ```c if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) { ``` Including horizontal TAB in this delimiter set makes parsing of `Set-Cookie: sess=SECRET;\tSecure` terminate in a way that prevents the following token from matching the recognized `Secure` attribute. curl therefore persists the cookie with the secure field set to `FALSE`. When that cookie jar is loaded for a later request to plaintext `http://tab-cookie.invalid`, curl considers it eligible and emits the secret in the `Cookie` request header. Fix commit [`4f6aa41a0145e930e766775dbe860883d350aa0a`](https://github.com/curl/curl/commit/4f6aa41a0145e930e766775dbe860883d350aa0a) changes the scan delimiter to: ```c if(!curlx_str_cspn(&ptr, &name, ";\r\n=")) { ``` TAB is then handled as leading whitespace around the attribute name, allowing `Secure` to be recognized. The fixed cookie jar records `TRUE`, and curl omits the cookie from plaintext HTTP. The fix also adds upstream regression test 2885 for this exact TAB-before-Secure form. ## Reproduction Steps 1. Run `bundle/repro/reproduction_steps.sh` from any directory; it derives portable paths from its own location and honors `PRUVA_ROOT`. 2. The script reads `bundle/project_cache_context.json`, uses the required prepared repository path when available, resolves fixed commit `4f6aa41a0145e930e766775dbe860883d350aa0a` and its first parent, and verifies the vulnerable and fixed source hunks before building non-sanitized real curl command-line products. 3. For each isolated attempt, it starts an HTTPS TCP peer, maps the non-localhost name `tab-cookie.invalid` to that peer with curl `--resolve`, and sends the literal wire header `Set-Cookie: sess=SECRET;\x09Secure`. It then starts a fresh plaintext HTTP peer, loads the resulting cookie jar with the same product build, and captures the request bytes. 4. It requires two vulnerable attempts to show `secure=FALSE` plus a captured plaintext `Cookie: sess=SECRET`, and two fixed attempts to show `secure=TRUE` with no Cookie header. All curl invocations are bounded by `timeout`. 5. On success it writes strict, target-bound `bundle/repro/runtime_manifest.json` with SHA-256 hashes of finalized evidence files. Exit status 0 means the issue was confirmed; status 1 means the required divergence was not reproduced. Expected terminal output includes: ```text PASS vulnerable attempt 1: secure=FALSE plaintext_cookie_seen=true PASS vulnerable attempt 2: secure=FALSE plaintext_cookie_seen=true PASS fixed attempt 1: secure=TRUE plaintext_cookie_seen=false PASS fixed attempt 2: secure=TRUE plaintext_cookie_seen=false CONFIRMED: vulnerable curl disclosed sess=SECRET over plaintext HTTP in two attempts; fixed curl blocked it in two attempts. ``` ## Evidence - `bundle/repro/vulnerable-attempt-1.log` — vulnerable transcript, SHA-256 `e995fcb7f7e5422788e3fc188312333fc71aa3bfd1b01c9d57d0586a9bfdfafd`. - `bundle/repro/vulnerable-attempt-2.log` — second vulnerable transcript, SHA-256 `ba0967b31d8a4e8657df1bdb6b7fd788529a2524cb3d7cba0f5c2865a681edc8`. - `bundle/repro/fixed-attempt-1.log` — fixed negative control, SHA-256 `b30e5a532dc4390fb7b122b44d2a5aba4128e46a457e5e8870601f34a3ba6b29`. - `bundle/repro/fixed-attempt-2.log` — second fixed negative control, SHA-256 `c5afc0185862af91e1b0f899fcdea3cb4098635dae13fe95a886509477807201`. - `bundle/repro/target-identity.log` — exact commits, patch lines, binary hashes, and product versions, SHA-256 `9b2d64973b3600d6852c952279942e6e51c36e7a84f71e7a88f18269658c8488`. - `bundle/repro/runtime_manifest.json` — entrypoint state, vulnerable source identity, proof artifact list, and checked hashes. - `bundle/logs/reproduction_steps.log` — overall diagnostics (not hashed as immutable proof because shell-wide `tee` writes it through script exit). - `bundle/logs/cmake-vulnerable.log`, `build-vulnerable.log`, `cmake-fixed.log`, and `build-fixed.log` — build diagnostics. The critical vulnerable excerpt is: ```text COMMIT=621e507300e6c83966567b6ae0352ca82544df13 ATTACKER_HEADER_HEX=5365742d436f6f6b69653a20736573733d5345435245543b09536563757265 COOKIE_JAR_SECURE_FIELD=FALSE PLAINTEXT_COOKIE_HEADER_SEEN=true ... Host: tab-cookie.invalid: Cookie: sess=SECRET ``` The fixed negative control contains: ```text COMMIT=4f6aa41a0145e930e766775dbe860883d350aa0a COOKIE_JAR_SECURE_FIELD=TRUE PLAINTEXT_COOKIE_HEADER_SEEN=false ``` Its captured plaintext request ends after `Accept: */*` and contains no `Cookie` header. Both peer transcripts record `LISTENING`, `ACCEPTED`, protocol request bytes, and the exact malicious header bytes. The tested environment was Linux x86-64, curl/libcurl `8.22.0-DEV` at the exact commits, OpenSSL 3.5.5, and nghttp2 1.68.0. The target digest is SHA-256 of `git:https://github.com/curl/curl@621e507300e6c83966567b6ae0352ca82544df13`: `99769c35eb8a8ef5d8c6f984733cb9a16c536698292048e4b4ef2b6fe755f495`. ## Recommendations / Next Steps - Upgrade to curl 8.22.0 or later, or apply the complete change from fixed commit `4f6aa41a0145e930e766775dbe860883d350aa0a` to affected downstream branches. - Do not treat TAB as a terminal name delimiter before cookie attribute matching; trim accepted optional whitespace and then compare the complete attribute token. - Preserve an end-to-end regression test with the literal `0x09` byte before `Secure`, a non-localhost origin, a persisted cookie jar, and a later plaintext HTTP request. Testing only the jar is weaker than proving that the secret is not emitted. - Include both a vulnerable regression fixture and a fixed negative control to avoid false conclusions caused by curl's special secure-cookie treatment of localhost/loopback origins. ## Additional Notes - **Idempotency confirmation:** The final script completed successfully twice consecutively. Each execution itself performed two clean vulnerable attempts and two clean fixed attempts. - **Real boundary:** The primary proof invokes the real curl command-line product. Attacker input crosses accepted HTTPS TCP connections, and disclosure crosses separate plaintext HTTP TCP connections; the vulnerable parser is not called directly by a unit harness. - **Sanitizers:** None were used. This is a protocol/confidentiality defect rather than a memory-safety crash. - **Edge case avoided:** The peer listens on loopback for containment, but requests use `tab-cookie.invalid` via `--resolve`. Using `127.0.0.1` as the URL host would be an invalid negative control because curl intentionally permits Secure cookies in localhost-like contexts. - **Version string nuance:** The exact fix parent and fix commit both report the development version string `8.22.0-DEV`; commit hashes and binary SHA-256 values, not that mutable prose label, bind the two tested builds. ## Reproduction Details Reproduced: 2026-09-11T10:13:16.872Z Duration: 1576 seconds Tool calls: 190 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00347 pruva-verify CVE-2026-80255 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00347&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00347/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-80255 - Source: https://curl.se/docs/CVE-2026-80255.html ## Artifacts - bundle/repro/rca_report.md (analysis, 8892 bytes) - bundle/repro/reproduction_steps.sh (reproduction_script, 17563 bytes) - bundle/repro/fixed-attempt-1.log (log, 2676 bytes) - bundle/repro/fixed-attempt-2.log (log, 2676 bytes) - bundle/repro/runtime_manifest.json (other, 1729 bytes) - bundle/repro/target-identity.log (log, 650 bytes) - bundle/repro/validation_verdict.json (other, 1219 bytes) - bundle/repro/vulnerable-attempt-1.log (log, 2751 bytes) - bundle/repro/vulnerable-attempt-2.log (log, 2751 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00347 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00347/artifacts/bundle/repro/reproduction_steps.sh - Web: https://www.pruva.dev/reproductions/REPRO-2026-00347 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://www.pruva.dev