GHSA-WWV5-G3V4-889X

Vulnerability from github – Published: 2026-09-01 20:17 – Updated: 2026-09-01 20:17
VLAI
Summary
Tornado: Incomplete fix for CVE-2026-35536: cookie attribute injection re-opened via the legacy case-insensitive `**kwargs` path in `set_cookie`
Details

Summary

The CVE-2026-35536 fix added a validation loop that rejects [\x00-\x20\x3b\x7f], but only for the hardcoded lowercase keys name/domain/path/samesite. The still-live deprecated **kwargs path writes attacker-supplied attribute values straight into the Morsel with no validation, and because Morsel.__setitem__ is case-insensitive, a capitalized kwarg (Domain=, Path=, SameSite=, Max-Age=) routes to the same reserved attribute while bypassing the loop — re-opening ;-delimited attribute injection.

self.set_cookie("sid", "abc", Domain="evil.com; Secure; SameSite=None")
#  -> Set-Cookie: sid=abc; Domain=evil.com; Secure; SameSite=None; Path=/
# Sanity (the canonical lowercase named arg IS blocked):
self.set_cookie("sid", "abc", domain="evil.com; Secure")   # -> http.cookies.CookieError

The patch's regression test (SetCookieForbiddenCharHandler) only exercises the four named params, never the **kwargs path, so the gap is not regression-covered.

Affected code

  • tornado/web.pyRequestHandler.set_cookie: the validation loop covers only the lowercase named args; the trailing if kwargs: loop does morsel[k] = v with no character validation.

Steps to reproduce

GET /upper (uses Domain= kwarg) emits Set-Cookie: c_upper=v; Domain=evil.com; Secure; SameSite=None; Path=/; GET /lower (uses lowercase domain=) returns a CookieError.

Impact

Injection of independent cookie attributes (force/drop Secure/HttpOnly/SameSite, rebind Domain/Path) — the same impact CVE-2026-35536 closed, via the sibling path the patch missed. Conditional on the app using a capitalized/legacy keyword.

Suggested remediation

Apply the same [\x00-\x20\x3b\x7f] validation to every entry in the **kwargs loop (after normalizing the key case), or remove the deprecated kwargs path; add a regression test for capitalized kwargs.

Credit

Reported as part of an incomplete-patch measurement study (responsible disclosure).

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "tornado"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.5.5"
            },
            {
              "fixed": "6.5.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-74"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-01T20:17:23Z",
    "nvd_published_at": null,
    "severity": "LOW"
  },
  "details": "## Summary\nThe CVE-2026-35536 fix added a validation loop that rejects `[\\x00-\\x20\\x3b\\x7f]`, but only for the\nhardcoded **lowercase** keys `name`/`domain`/`path`/`samesite`. The still-live deprecated `**kwargs` path\nwrites attacker-supplied attribute values straight into the `Morsel` with no validation, and because\n`Morsel.__setitem__` is case-insensitive, a capitalized kwarg (`Domain=`, `Path=`, `SameSite=`, `Max-Age=`)\nroutes to the same reserved attribute while bypassing the loop \u2014 re-opening `;`-delimited attribute injection.\n\n```python\nself.set_cookie(\"sid\", \"abc\", Domain=\"evil.com; Secure; SameSite=None\")\n#  -\u003e Set-Cookie: sid=abc; Domain=evil.com; Secure; SameSite=None; Path=/\n# Sanity (the canonical lowercase named arg IS blocked):\nself.set_cookie(\"sid\", \"abc\", domain=\"evil.com; Secure\")   # -\u003e http.cookies.CookieError\n```\n\nThe patch\u0027s regression test (`SetCookieForbiddenCharHandler`) only exercises the four named params, never the\n`**kwargs` path, so the gap is not regression-covered.\n\n## Affected code\n- `tornado/web.py` \u2192 `RequestHandler.set_cookie`: the validation loop covers only the lowercase named args;\n  the trailing `if kwargs:` loop does `morsel[k] = v` with no character validation.\n\n## Steps to reproduce\n`GET /upper` (uses `Domain=` kwarg) emits `Set-Cookie: c_upper=v; Domain=evil.com; Secure; SameSite=None; Path=/`; `GET /lower` (uses lowercase\n`domain=`) returns a `CookieError`.\n\n## Impact\nInjection of independent cookie attributes (force/drop `Secure`/`HttpOnly`/`SameSite`, rebind `Domain`/`Path`)\n\u2014 the same impact CVE-2026-35536 closed, via the sibling path the patch missed. Conditional on the app using\na capitalized/legacy keyword.\n\n## Suggested remediation\nApply the same `[\\x00-\\x20\\x3b\\x7f]` validation to every entry in the `**kwargs` loop (after normalizing the\nkey case), or remove the deprecated kwargs path; add a regression test for capitalized kwargs.\n\n## Credit\nReported as part of an incomplete-patch measurement study (responsible disclosure).",
  "id": "GHSA-wwv5-g3v4-889x",
  "modified": "2026-09-01T20:17:23Z",
  "published": "2026-09-01T20:17:23Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/security/advisories/GHSA-wwv5-g3v4-889x"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/pull/3704"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/pull/3706"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/commit/6ef836e43e1278530041376adb32504daa977b91"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/commit/da284767eae8e1f0484f123b8c3225f6465b09c7"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/tornadoweb/tornado"
    },
    {
      "type": "WEB",
      "url": "https://github.com/tornadoweb/tornado/releases/tag/v6.5.8"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Tornado: Incomplete fix for CVE-2026-35536: cookie attribute injection re-opened via the legacy case-insensitive `**kwargs` path in `set_cookie`"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…