{"id":"CVE-2026-44889","aliases":["GHSA-fh3h-vg37-cc95","PYSEC-2026-251"],"url":"https://o3.security/vulnerability/CVE-2026-44889","summary":"WebOb: Location header normalization during redirect leads to open redirect","details":"### Impact\n\nWhen WebOb normalizes the HTTP Location header to include the request hostname, it does so by parsing the URL that the user is to be redirected to with Python's `urllib.parse`, and joining it to the base URL. `urlsplit` (called internally by `urljoin`) however treats a `//` at the start of a string as a URI without a scheme, and then treats the next part as the hostname. `urljoin` will then use that hostname from the second part as the hostname replacing the original one from the request.\n\nIn a previous advisory https://github.com/Pylons/webob/security/advisories/GHSA-mg3v-6m49-jhp3 an attempt to fix this was made by forcing the replacement of `//` with `/%2f`, however this did not take into account that since Python 3.10 `urlsplit` internally strips ASCII tab, carriage return, and newline characters from the string, so `/\\t/attacker.com` gets turned into `//attacker.com` and the attacker is able to bypass the changes introduced in that previous advisory, thereby bringing back the problem that was attempted to be fixed.\n\n```\n>>> parse.urlparse(\"//attacker.com/some/path\")\nParseResult(scheme='', netloc='attacker.com', path='/some/path', params='', query='', fragment='')\n```\n\nWebOb uses `urljoin` to take the request URI and join the redirect location to it, so assuming the request URI is `https://example.org/` and the URL to redirect to is `/\\t/attacker.com/some/path/`:\n\n```\n>>> parse.urljoin(\"https://example.org/\", \"/\\t/attacker.com/some/path/\")\n'https://attacker.com/some/path/'\n```\n\nWhich redirects from `example.org` where we want the user to stay to `attacker.com`.\n\n### Patches\n\nThis issue has been fixed in WebOb 1.8.10.\n\n### Workarounds\n\nAny use of the `Response` class that includes a `location` can be rewritten to make sure to always pass a full URI that includes the hostname to redirect the user to, or to validate that the redirect target starts with a scheme (e.g. `http://` or `https://`) before assigning to `Response.location`.\n\n### References\n\n- https://github.com/Pylons/webob/security/advisories/GHSA-mg3v-6m49-jhp3\n- CVE-2024-42353\n\n### Thanks\n\n- Caleb Brown of Google","published":"2026-06-22T21:30:11.789Z","modified":"2026-08-12T03:51:48.434747946Z","cvss":{"score":6.1,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"},"epss":{"score":0.0021,"percentile":0.11468,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"webob","fixedVersion":"1.8.10"}],"fix":{"url":"https://github.com/Pylons/webob/commit/1f681a4f17fc10777ef861e8b43ecb26053bc539","label":"Pylons/webob@1f681a4"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44889.json"},{"type":"ADVISORY","url":"https://github.com/Pylons/webob/security/advisories/GHSA-fh3h-vg37-cc95"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44889"},{"type":"WEB","url":"https://github.com/Pylons/webob/security/advisories/GHSA-mg3v-6m49-jhp3"},{"type":"WEB","url":"https://github.com/Pylons/webob/commit/1f681a4f17fc10777ef861e8b43ecb26053bc539"},{"type":"WEB","url":"https://github.com/Pylons/webob/commit/2b9fbedafb31180c910cf8526e9ea72b4603d0bc"},{"type":"PACKAGE","url":"https://github.com/Pylons/webob"},{"type":"WEB","url":"https://github.com/Pylons/webob/releases/tag/1.8.10"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/webob/PYSEC-2026-251.yaml"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:48.434747946Z"}}