# Prowler: SAML Domain Claiming Enables Cross-Tenant Account Takeover

Prowler's SAML ACS finish derived the tenant from the asserted email domain instead of the validated SAML config, letting a malicious tenant's IdP obtain a victim-tenant token.

- Payout: Research
- Severity: critical
- Program: Prowler
- Found by: EQSTLab, josema-xyz
- Disclosed: 2026-09-11
- CVE: CVE-2026-59151
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-h8m9-jgf8-vwvp
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-h8m9-jgf8-vwvp
- Tags: area:web domain:web impact:ato impact:data-leak kind:research platform:ghsa sev:critical tech:python tech:saml technique:chained technique:source-review vuln:access-control vuln:account-takeover vuln:auth-bypass

**Target:** Django/allauth SAML ACS endpoint and token issuance API
**Root cause:** The ACS finish flow recalculated the tenant via `user.email.split("@")[-1]` and a global SAMLConfiguration lookup instead of binding token issuance to the SAML configuration selected by the ACS route. Compounded by no domain-ownership proof, hardcoded SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT, and reject_idp_initiated_sso=False.
**Steps:**
1. Attacker registers a tenant and POSTs `/api/v1/saml-config` with `email_domain=attacker.com` and attacker-controlled IdP metadata XML.
2. Attacker's IdP issues a signed SAMLResponse asserting NameID `user@victim.com`, posted to `POST /api/v1/accounts/saml/attacker.com/acs/` as `SAMLResponse=$SIGNED_ASSERTION`.
3. ACS finish logic derives tenant from `user.email.split("@")[-1]` and resolves the victim tenant's SAMLConfiguration.
4. Exchange the temporary SAML token: `POST /api/v1/tokens/saml?id=$SAML_TOKEN_ID` returns victim JWT.
5. Switch into victim tenant: `POST /api/v1/tokens/switch` with `tenant_id=<victim-real-tenant-uuid>`.
**How found:** Code review of the SAML ACS finish flow: noticed tenant was recalculated from the asserted `user.email` domain rather than the SAML configuration validated for the ACS route, then traced compounding settings (auto-connect, IdP-initiated SSO) and the global SAMLDomainIndex to confirm cross-tenant token issuance.
**Exploitation:** Attacker needs an authenticated Prowler account with a tenant and a self-controlled SAML IdP. They configure SAML for their own domain, then post an IdP-initiated SAMLResponse signed with their own cert asserting a victim email. Because auto-connect is hardcoded on and IdP-initiated SSO is accepted, the adapter links the session to the existing victim user and a SAMLToken is issued; exchanging it yields a victim JWT, and the token-switch endpoint pivots into any tenant the victim belongs to.
**Escalation:** Token switch endpoint (`/api/v1/tokens/switch`) allows lateral movement into every tenant the victim user is a member of.
**Insight:** In multi-tenant SSO, grep for tenant resolution that re-derives identity from assertion fields (email domain, NameID) after the route already selected a config; the route-selected config's tenant must be the only source of truth for token issuance.

