{"id":"GHSA-gcwf-c25f-p9rv","summary":"Kiwi TCMS account confirmation open redirect through protocol-relative URLs","details":"## Summary\n\nIn Kiwi TCMS version 16.1, the fix for CVE-2026-54724 (open redirect via unvalidated `next` parameter) in the account confirmation endpoint is incomplete. The validation uses `startswith(\"/\")` to check if the redirect URL is local, but this is bypassed by protocol-relative URLs like `//evil.com` which start with `/` but redirect the browser to an external attacker-controlled domain. 16.1’s incomplete mitigation still allows protocol-relative destinations and earlier releases lack the mitigation.\n\n## Vulnerable Code\n\nIn `tcms/kiwi_auth/views.py`, the `Confirm` view (line ~113):\n\n    def get_redirect_url(self, *args, **kwargs):\n        self.url = self.request.GET.get(\"next\", reverse(\"core-views-index\"))\n        if not self.url.startswith(\"/\"):\n            self.url = reverse(\"core-views-index\")\n\nThe check `startswith(\"/\")` passes for `//evil.com` because it begins with `/`, but browsers interpret `//evil.com` as a protocol-relative URL that redirects to `https://evil.com` (or `http://evil.com` depending on the current page's protocol).\n\n## Proof of Concept\n\n    GET /confirm/\u003cactivation_key\u003e/?next=//evil.com/phishing HTTP/1.1\n    Host: kiwi.example.com\n\nResponse:\n\n    HTTP/1.1 302 Found\n    Location: //evil.com/phishing\n\nThe browser follows the redirect to `https://evil.com/phishing`.\n\nVerification:\n\n    \u003e\u003e\u003e url = \"//evil.com/phishing\"\n    \u003e\u003e\u003e url.startswith(\"/\")\n    True  # passes the check!\n\n## Impact\n\nAn attacker can craft a confirmation link that, after the user confirms their account, redirects them to a phishing page. This is useful for:\n\n- Credential phishing (fake login page on attacker domain)\n- Malware delivery after trusted action\n- Social engineering (user trusts the link because it's from the legitimate Kiwi TCMS instance)\n\nThe attack requires sending a crafted activation link to a user who is registering, or intercepting/predicting the activation key.\n\n## Suggested Fix\n\nUse Django's `url_has_allowed_host_and_scheme()` utility instead of a simple `startswith` check:\n\n    from django.utils.http import url_has_allowed_host_and_scheme\n\n    def get_redirect_url(self, *args, **kwargs):\n        self.url = self.request.GET.get(\"next\", reverse(\"core-views-index\"))\n        if not url_has_allowed_host_and_scheme(\n            self.url,\n            allowed_hosts={self.request.get_host()},\n            require_https=self.request.is_secure(),\n        ):\n            self.url = reverse(\"core-views-index\")\n\nThis properly rejects protocol-relative URLs, absolute URLs to other hosts, and other bypass patterns.\n\nFixed in Version 16.2","aliases":["CVE-2026-77433"],"modified":"2026-11-09T23:30:04.658688800Z","published":"2026-11-09T23:24:14Z","database_specific":{"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-11-09T23:24:14Z","nvd_published_at":null,"cwe_ids":["CWE-601"]},"references":[{"type":"WEB","url":"https://github.com/kiwitcms/Kiwi/security/advisories/GHSA-gcwf-c25f-p9rv"},{"type":"WEB","url":"https://github.com/kiwitcms/Kiwi/security/advisories/GHSA-hmj5-jm8h-h9fh"},{"type":"WEB","url":"https://github.com/kiwitcms/Kiwi/pull/4458"},{"type":"WEB","url":"https://github.com/kiwitcms/Kiwi/commit/65d8f7c2743d7f06a4fca77c060b9d561f02f948"},{"type":"PACKAGE","url":"https://github.com/kiwitcms/Kiwi"},{"type":"WEB","url":"https://github.com/kiwitcms/Kiwi/releases/tag/v16.2"}],"affected":[{"package":{"name":"kiwitcms","ecosystem":"PyPI","purl":"pkg:pypi/kiwitcms"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"12.4"}]}],"versions":["10.0","10.1","10.2","10.3","10.3.999","10.4","10.5","11.0","11.1","11.3","11.4","11.5","11.6","11.7","12.0","12.1","12.2","12.3","12.4","6.10","6.11","6.2.1","6.3","6.4","6.5","6.5.3","6.6","6.7","6.8","6.9","7.0","7.1","7.2","7.2.1","7.3","8.0","8.1","8.1.99","8.2","8.3","8.4","8.5","8.6","8.6.1","8.7","8.8","8.9","9.0","9.999"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-gcwf-c25f-p9rv/GHSA-gcwf-c25f-p9rv.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"}]}