diff --git a/authentik/providers/oauth2/tests/test_authorize.py b/authentik/providers/oauth2/tests/test_authorize.py index 91cdc330a..fcaddff7c 100644 --- a/authentik/providers/oauth2/tests/test_authorize.py +++ b/authentik/providers/oauth2/tests/test_authorize.py @@ -85,6 +85,25 @@ class TestAuthorize(OAuthTestCase): ) OAuthAuthorizationParams.from_request(request) + def test_blocked_redirect_uri(self): + """test missing/invalid redirect URI""" + OAuth2Provider.objects.create( + name=generate_id(), + client_id="test", + authorization_flow=create_test_flow(), + redirect_uris="data:local.invalid", + ) + with self.assertRaises(RedirectUriError): + request = self.factory.get( + "/", + data={ + "response_type": "code", + "client_id": "test", + "redirect_uri": "data:localhost", + }, + ) + OAuthAuthorizationParams.from_request(request) + def test_invalid_redirect_uri_empty(self): """test missing/invalid redirect URI""" provider = OAuth2Provider.objects.create( diff --git a/authentik/providers/oauth2/views/authorize.py b/authentik/providers/oauth2/views/authorize.py index 8c31192bf..2bfa92e3d 100644 --- a/authentik/providers/oauth2/views/authorize.py +++ b/authentik/providers/oauth2/views/authorize.py @@ -74,6 +74,7 @@ PLAN_CONTEXT_PARAMS = "goauthentik.io/providers/oauth2/params" SESSION_KEY_LAST_LOGIN_UID = "authentik/providers/oauth2/last_login_uid" ALLOWED_PROMPT_PARAMS = {PROMPT_NONE, PROMPT_CONSENT, PROMPT_LOGIN} +FORBIDDEN_URI_SCHEMES = {"javascript", "data", "vbscript"} @dataclass(slots=True) @@ -174,6 +175,10 @@ class OAuthAuthorizationParams: self.check_scope() self.check_nonce() self.check_code_challenge() + if self.request: + raise AuthorizeError( + self.redirect_uri, "request_not_supported", self.grant_type, self.state + ) def check_redirect_uri(self): """Redirect URI validation.""" @@ -211,10 +216,9 @@ class OAuthAuthorizationParams: expected=allowed_redirect_urls, ) raise RedirectUriError(self.redirect_uri, allowed_redirect_urls) - if self.request: - raise AuthorizeError( - self.redirect_uri, "request_not_supported", self.grant_type, self.state - ) + # Check against forbidden schemes + if urlparse(self.redirect_uri).scheme in FORBIDDEN_URI_SCHEMES: + raise RedirectUriError(self.redirect_uri, allowed_redirect_urls) def check_scope(self): """Ensure openid scope is set in Hybrid flows, or when requesting an id_token""" diff --git a/authentik/providers/oauth2/views/token.py b/authentik/providers/oauth2/views/token.py index d9d82ca8c..5a9d2c8ce 100644 --- a/authentik/providers/oauth2/views/token.py +++ b/authentik/providers/oauth2/views/token.py @@ -6,6 +6,7 @@ from hashlib import sha256 from re import error as RegexError from re import fullmatch from typing import Any, Optional +from urllib.parse import urlparse from django.http import HttpRequest, HttpResponse from django.utils import timezone @@ -53,6 +54,7 @@ from authentik.providers.oauth2.models import ( RefreshToken, ) from authentik.providers.oauth2.utils import TokenResponse, cors_allow, extract_client_auth +from authentik.providers.oauth2.views.authorize import FORBIDDEN_URI_SCHEMES from authentik.sources.oauth.models import OAuthSource from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS @@ -204,6 +206,10 @@ class TokenParams: ).from_http(request) raise TokenError("invalid_client") + # Check against forbidden schemes + if urlparse(self.redirect_uri).scheme in FORBIDDEN_URI_SCHEMES: + raise TokenError("invalid_request") + self.authorization_code = AuthorizationCode.objects.filter(code=raw_code).first() if not self.authorization_code: LOGGER.warning("Code does not exist", code=raw_code) diff --git a/website/docs/releases/2023/v2023.8.md b/website/docs/releases/2023/v2023.8.md index cacd8772a..b89da8923 100644 --- a/website/docs/releases/2023/v2023.8.md +++ b/website/docs/releases/2023/v2023.8.md @@ -159,6 +159,14 @@ image: - \*: fix [GHSA-rjvp-29xq-f62w](../security/GHSA-rjvp-29xq-f62w), Reported by [@devSparkle](https://github.com/devSparkle) +## Fixed in 2023.8.5 + +- security: fix [CVE-2023-48228](../../security/CVE-2023-48228.md), Reported by [@Sapd](https://github.com/Sapd) (#7666) + +## Fixed in 2023.8.6 + +- providers/oauth2: fix [CVE-2024-21637](../../security/CVE-2024-21637.md), Reported by [@lauritzh](https://github.com/lauritzh) (#8104) + ## API Changes #### What's New diff --git a/website/docs/security/CVE-2024-21637.md b/website/docs/security/CVE-2024-21637.md new file mode 100644 index 000000000..f7322ed2e --- /dev/null +++ b/website/docs/security/CVE-2024-21637.md @@ -0,0 +1,39 @@ +# CVE-2024-21637 + +_Reported by [@lauritzh](https://github.com/lauritzh)_ + +## XSS in Authentik via JavaScript-URI as Redirect URI and form_post Response Mode + +### Summary + +Given an OAuth2 provider configured with allowed redirect URIs set to `*` or `.*`, an attacker can send an OAuth Authorization request using `response_mode=form_post` and setting `redirect_uri` to a malicious URI, to capture authentik's session token. + +### Patches + +authentik 2023.8.6 and 2023.10.6 fix this issue. + +### Impact + +The impact depends on the attack scenario. In the following I will describe the two scenario that were identified for Authentik. + +#### Redirect URI Misconfiguration + +While advising that this may cause security issues, Authentik generally allows wildcards as Redirect URI. Therefore, using a wildcard-only effectively allowing arbitrary URLS is possible misconfiguration that may be present in real-world instances. + +In such cases, unauthenticated and unprivileged attackers can perform the above described actions. + +### User with (only) App Administration Permissions + +A more likely scenario is an administrative user (e.g. a normal developer) having only permissions to manage applications. + +This relatively user could use the described attacks to perform a privilege escalation. + +### Workaround + +It is recommended to upgrade to the patched version of authentik. If not possible, ensure that OAuth2 providers do not use a wildcard (`*` or `.*`) value as allowed redirect URI setting. (This is _not_ exploitable if part of the redirect URI has a wildcard, for example `https://foo-.*\.bar\.com`) + +### For more information + +If you have any questions or comments about this advisory: + +- Email us at [security@goauthentik.io](mailto:security@goauthentik.io) diff --git a/website/sidebars.js b/website/sidebars.js index e387f27cd..196ea0d08 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -362,6 +362,7 @@ const docsSidebar = { }, items: [ "security/policy", + "security/CVE-2024-21637", "security/CVE-2023-48228", "security/GHSA-rjvp-29xq-f62w", "security/CVE-2023-39522",