diff --git a/authentik/providers/oauth2/tests/test_introspect.py b/authentik/providers/oauth2/tests/test_introspect.py index 26a8001644c3..374260a52793 100644 --- a/authentik/providers/oauth2/tests/test_introspect.py +++ b/authentik/providers/oauth2/tests/test_introspect.py @@ -29,7 +29,6 @@ def setUp(self) -> None: self.app = Application.objects.create( name=generate_id(), slug=generate_id(), provider=self.provider ) - self.app.save() self.user = create_test_admin_user() self.auth = b64encode( f"{self.provider.client_id}:{self.provider.client_secret}".encode() @@ -114,6 +113,41 @@ def test_introspect_invalid_token(self): }, ) + def test_introspect_invalid_provider(self): + """Test introspection (mismatched provider and token)""" + provider: OAuth2Provider = OAuth2Provider.objects.create( + name=generate_id(), + authorization_flow=create_test_flow(), + redirect_uris="", + signing_key=create_test_cert(), + ) + auth = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode() + + token: AccessToken = AccessToken.objects.create( + provider=self.provider, + user=self.user, + token=generate_id(), + auth_time=timezone.now(), + _scope="openid user profile", + _id_token=json.dumps( + asdict( + IDToken("foo", "bar"), + ) + ), + ) + res = self.client.post( + reverse("authentik_providers_oauth2:token-introspection"), + HTTP_AUTHORIZATION=f"Basic {auth}", + data={"token": token.token}, + ) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "active": False, + }, + ) + def test_introspect_invalid_auth(self): """Test introspect (invalid auth)""" res = self.client.post( diff --git a/authentik/providers/oauth2/views/introspection.py b/authentik/providers/oauth2/views/introspection.py index 7c2d096f7047..51b18f6e9335 100644 --- a/authentik/providers/oauth2/views/introspection.py +++ b/authentik/providers/oauth2/views/introspection.py @@ -46,10 +46,10 @@ def from_request(request: HttpRequest) -> "TokenIntrospectionParams": if not provider: raise TokenIntrospectionError - access_token = AccessToken.objects.filter(token=raw_token).first() + access_token = AccessToken.objects.filter(token=raw_token, provider=provider).first() if access_token: return TokenIntrospectionParams(access_token, provider) - refresh_token = RefreshToken.objects.filter(token=raw_token).first() + refresh_token = RefreshToken.objects.filter(token=raw_token, provider=provider).first() if refresh_token: return TokenIntrospectionParams(refresh_token, provider) LOGGER.debug("Token does not exist", token=raw_token) diff --git a/website/docs/security/CVE-2024-47077.md b/website/docs/security/CVE-2024-47077.md new file mode 100644 index 000000000000..53f387efc6d7 --- /dev/null +++ b/website/docs/security/CVE-2024-47077.md @@ -0,0 +1,25 @@ +# CVE-2024-47077 + +_Reported by [@quentinmit](https://github.com/quentinmit)_ + +## Insufficient cross-provider token validation during introspection + +### Summary + +Access tokens issued to one application can be stolen by that application and used to impersonate the user against any other proxy provider. Also, a user can steal an access token they were legitimately issued for one application and use it to access another application that they aren't allowed to access. + +### Details + +The proxy provider uses `/application/o/introspect/` to validate bearer tokens provided in the `Authorization` header: + +The implementation of this endpoint separately validates the `client_id` and `client_secret` (which are that of the proxy provider) and the `token` without validating that they correspond to the same provider. + +### Patches + +authentik 2024.6.5 and 2024.8.3 fix this issue. + +### 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 a7c0b98a598c..14b46351fdbb 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -521,6 +521,7 @@ const docsSidebar = { items: [ "security/security-hardening", "security/policy", + "security/CVE-2024-47077", "security/CVE-2024-42490", "security/CVE-2024-38371", "security/CVE-2024-37905",