CVE-2023-39522 (Python, published 2023-08-29T18:15Z)
Description: value: goauthentik is an open-source Identity Provider. In affected versions using a recovery flow with an identification stage an attacker is able to determine if a username exists. Only setups configured with a recovery flow are impacted by this. Anyone with a user account on a system with the recovery flow described above is susceptible to having their username/email revealed as existing. An attacker can easily enumerate and check users' existence using the recovery flow, as a clear message is shown when a user doesn't exist. Depending on configuration this can either be done by username, email, or both. This issue has been addressed in versions 2023.5.6 and 2023.6.2. Users are advised to upgrade. There are no known workarounds for this issue.
Severity: nan (CVSS v3 base score: 5.3)
Weakness: CWE-203 \u2014 Observable Discrepancy
The product behaves differently or sends different responses under different circumstances in a way that is observable to an unauthorized actor, which exposes security-relevant information about the state of the product, such as whether a particular operation was successful or not.
Repository: https://github.com/goauthentik/authentik
Files changed:
authentik/stages/email/stage.py
authentik/stages/email/tests/test_sending.py
authentik/stages/identification/stage.py
authentik/stages/identification/tests.py
website/docs/releases/2023/v2023.5.md
website/docs/releases/2023/v2023.6.md
website/docs/security/CVE-2023-39522.md
website/sidebars.js
Commit message: security: fix CVE-2023-39522 (#6665)

* stages/email: don't disclose whether a user exists or not when recovering

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update website

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

Vulnerable code:
from authentik.flows.models import FlowToken
from rest_framework.test import APITestCase
class TestEmailStageSending(APITestCase):
    def test_recovery_flow(self):

Fixed code:
from authentik.flows.models import FlowDesignation, FlowToken
        if not pending_user.pk and self.executor.flow.designation == FlowDesignation.RECOVERY:
            # Pending user does not have a primary key, and we're in a recovery flow,
            # which means the user entered an invalid identifier, so we pretend to send the
            # email, to not disclose if the user exists
            return
from authentik.core.models import User
from authentik.flows.tests import FlowTestCase
from authentik.lib.generators import generate_id
class TestEmailStageSending(FlowTestCase):
            self.assertStageResponse(
                response,
                self.flow,
                response_errors={
                    "non_field_errors": [{"string": "email-sent", "code": "email-sent"}]
                },
            )
    def test_pending_fake_user(self):
        """Test with pending (fake) user"""
        self.flow.designation = FlowDesignation.RECOVERY
        self.flow.save()
        plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
        plan.context[PLAN_CONTEXT_PENDING_USER] = User(username=generate_id())
        session = self.client.session
        session[SESSION_KEY_PLAN] = plan
        session.save()

        url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
        with patch(
            "authentik.stages.email.models.EmailStage.backend_class",
            PropertyMock(return_value=EmailBackend),
        ):
            response = self.client.post(url)
            self.assertEqual(response.status_code, 200)
            self.assertStageResponse(
                response,
                self.flow,
                response_errors={
                    "non_field_errors": [{"string": "email-sent", "code": "email-sent"}]
                },
            )
            self.assertEqual(len(mail.outbox), 0)

            self.pre_user = self.stage.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
            if self.stage.executor.flow.designation == FlowDesignation.RECOVERY:
                # When used in a recovery flow, always continue to not disclose if a user exists
                return attrs
    def test_link_recovery_flow(self):
    def test_recovery_flow_invalid_user(self):
        """Test that an invalid user can proceed in a recovery flow"""
        self.flow.designation = FlowDesignation.RECOVERY
        self.flow.save()
        response = self.client.get(
            reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}),
        )
        self.assertStageResponse(
            response,
            self.flow,
            component="ak-stage-identification",
            user_fields=["email"],
            password_fields=False,
            show_source_labels=False,
            primary_action="Continue",
            sources=[
                {
                    "challenge": {
                        "component": "xak-flow-redirect",
                        "to": "/source/oauth/login/test/",
                        "type": ChallengeTypes.REDIRECT.value,
                    },
                    "icon_url": "/static/authentik/sources/default.svg",
                    "name": "test",
                }
            ],
        )
        form_data = {"uid_field": generate_id()}
        url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
        response = self.client.post(url, form_data)
        self.assertEqual(response.status_code, 200)

## Fixed in 2023.5.6

-   \*: fix [CVE-2023-39522](../security/CVE-2023-39522), Reported by [@markrassamni](https://github.com/markrassamni)

## Fixed in 2023.6.2

-   \*: fix [CVE-2023-39522](../security/CVE-2023-39522), Reported by [@markrassamni](https://github.com/markrassamni)

# CVE-2023-39522

_Reported by [@markrassamni](https://github.com/markrassamni)_

## Username enumeration attack

### Summary

Using a recovery flow with an identification stage an attacker is able to determine if a username exists.

### Patches

authentik 2023.5.6 and 2023.6.2 fix this issue.

### Impact

Only setups configured with a recovery flow are impacted by this.

### Details

An attacker can easily enumerate and check users' existence using the recovery flow, as a clear message is shown when a user doesn't exist. Depending on configuration this can either be done by username, email, or both.

### For more information

If you have any questions or comments about this advisory:

-   Email us at [security@goauthentik.io](mailto:security@goauthentik.io)
                "security/CVE-2023-39522",