CVE-2026-100169
Account pre-hijacking in the Prowler API through email based social account linking without verification
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4.0 before 5.35.0 allows a remote unauthenticated attacker to obtain persistent access to the account of a user who has not yet registered, because ProwlerSocialAccountAdapter.pre_social_login() in api/src/backend/api/adapters.py links a Google or GitHub login to any existing local account that has the same email address as the one returned by the provider, without requiring that the address has been verified by the provider or on the existing account, and without any other check that both accounts belong to the same person. Self registration is open and the new account is usable at once, because UserViewSet.get_permissions() in api/src/backend/api/v1/views.py grants AllowAny to the create action, ACCOUNT_EMAIL_VERIFICATION is set to none in api/src/backend/config/settings/social_login.py, and the Django ModelBackend checks only the password and is_active, not the django-allauth EmailAddress record. An attacker who registers an email address before its owner keeps the password set at registration after the link is made, because sociallogin.connect() does not reset the password and does not disable the existing account, and the owner who later signs in with Google or GitHub is logged into the attacker's account. django-allauth makes the same email based link on its own, because SOCIALACCOUNT_EMAIL_AUTHENTICATION and SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT are both set to True in the same settings module. This gives the attacker the cloud provider credentials, scan findings and tenant memberships held in the account.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- Passive
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.6
Account pre-hijacking in the Prowler API through email based social account linking without verification
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4.0 before 5.35.0 allows a remote unauthenticated attacker to obtain persistent access to the account of a user who has not yet registered, because ProwlerSocialAccountAdapter.pre_social_login() in api/src/backend/api/adapters.py links a Google or GitHub login to any existing local account that has the same email address as the one returned by the provider, without requiring that the address has been verified by the provider or on the existing account, and without any other check that both accounts belong to the same person. Self registration is open and the new account is usable at once, because UserViewSet.get_permissions() in api/src/backend/api/v1/views.py grants AllowAny to the create action, ACCOUNT_EMAIL_VERIFICATION is set to none in api/src/backend/config/settings/social_login.py, and the Django ModelBackend checks only the password and is_active, not the django-allauth EmailAddress record. An attacker who registers an email address before its owner keeps the password set at registration after the link is made, because sociallogin.connect() does not reset the password and does not disable the existing account, and the owner who later signs in with Google or GitHub is logged into the attacker's account. django-allauth makes the same email based link on its own, because SOCIALACCOUNT_EMAIL_AUTHENTICATION and SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT are both set to True in the same settings module. This gives the attacker the cloud provider credentials, scan findings and tenant memberships held in the account.
- Record
CVE-2026-100169- CVSS 4.0
- 7.6 High
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-287
- Updated
- 2026-11-02
EUVDEUVD-2026-101295
7.6
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4.0 before 5.35.0 allows a remote unauthenticated attacker to obtain persistent access to the account of a user who has not yet registered, because ProwlerSocialAccountAdapter.pre_social_login() in api/src/backend/api/adapters.py links a Google or GitHub login to any existing local account that has the same email address as the one returned by the provider, without requiring that the address has been verified by the provider or on the existing account, and without any other check that both accounts belong to the same person. Self registration is open and the new account is usable at once, because UserViewSet.get_permissions() in api/src/backend/api/v1/views.py grants AllowAny to the create action, ACCOUNT_EMAIL_VERIFICATION is set to none in api/src/backend/config/settings/social_login.py, and the Django ModelBackend checks only the password and is_active, not the django-allauth EmailAddress record. An attacker who registers an email address before its owner keeps the password set at registration after the link is made, because sociallogin.connect() does not reset the password and does not disable the existing account, and the owner who later signs in with Google or GitHub is logged into the attacker's account. django-allauth makes the same email based link on its own, because SOCIALACCOUNT_EMAIL_AUTHENTICATION and SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT are both set to True in the same settings module. This gives the attacker the cloud provider credentials, scan findings and tenant memberships held in the account.
- Record
EUVD-2026-101295- CVSS 4.0
- 7.6 High
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-5qw5-54v9-9g7q
7.6
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4...
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4.0 before 5.35.0 allows a remote unauthenticated attacker to obtain persistent access to the account of a user who has not yet registered, because ProwlerSocialAccountAdapter.pre_social_login() in api/src/backend/api/adapters.py links a Google or GitHub login to any existing local account that has the same email address as the one returned by the provider, without requiring that the address has been verified by the provider or on the existing account, and without any other check that both accounts belong to the same person. Self registration is open and the new account is usable at once, because UserViewSet.get_permissions() in api/src/backend/api/v1/views.py grants AllowAny to the create action, ACCOUNT_EMAIL_VERIFICATION is set to none in api/src/backend/config/settings/social_login.py, and the Django ModelBackend checks only the password and is_active, not the django-allauth EmailAddress record. An attacker who registers an email address before its owner keeps the password set at registration after the link is made, because sociallogin.connect() does not reset the password and does not disable the existing account, and the owner who later signs in with Google or GitHub is logged into the attacker's account. django-allauth makes the same email based link on its own, because SOCIALACCOUNT_EMAIL_AUTHENTICATION and SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT are both set to True in the same settings module. This gives the attacker the cloud provider credentials, scan findings and tenant memberships held in the account.
- Record
GHSA-5qw5-54v9-9g7q- CVSS 4.0
- 7.6 High
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-287
- Updated
- 2026-11-02
NVD
7.6
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4.0 before 5.35.0 allows a remote unauthenticated attacker to obtain persistent access to the account of a user who has not yet registered, because ProwlerSocialAccountAdapter.pre_social_login() in api/src/backend/api/adapters.py links a Google or GitHub login to any existing local account that has the same email address as the one returned by the provider, without requiring that the address has been verified by the provider or on the existing account, and without any other check that both accounts belong to the same person. Self registration is open and the new account is usable at once, because UserViewSet.get_permissions() in api/src/backend/api/v1/views.py grants AllowAny to the create action, ACCOUNT_EMAIL_VERIFICATION is set to none in api/src/backend/config/settings/social_login.py, and the Django ModelBackend checks only the password and is_active, not the django-allauth EmailAddress record. An attacker who registers an email address before its owner keeps the password set at registration after the link is made, because sociallogin.connect() does not reset the password and does not disable the existing account, and the owner who later signs in with Google or GitHub is logged into the attacker's account. django-allauth makes the same email based link on its own, because SOCIALACCOUNT_EMAIL_AUTHENTICATION and SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT are both set to True in the same settings module. This gives the attacker the cloud provider credentials, scan findings and tenant memberships held in the account.
- Record
CVE-2026-100169- CVSS 4.0
- 7.6 High
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-287
- Updated
- 2026-11-02
OSV
7.6
Account pre-hijacking in the Prowler API through email based social account linking without verification
Improper Authentication in the social account linking path of the Prowler API in Prowler from 5.4.0 before 5.35.0 allows a remote unauthenticated attacker to obtain persistent access to the account of a user who has not yet registered, because ProwlerSocialAccountAdapter.pre_social_login() in api/src/backend/api/adapters.py links a Google or GitHub login to any existing local account that has the same email address as the one returned by the provider, without requiring that the address has been verified by the provider or on the existing account, and without any other check that both accounts belong to the same person. Self registration is open and the new account is usable at once, because UserViewSet.get_permissions() in api/src/backend/api/v1/views.py grants AllowAny to the create action, ACCOUNT_EMAIL_VERIFICATION is set to none in api/src/backend/config/settings/social_login.py, and the Django ModelBackend checks only the password and is_active, not the django-allauth EmailAddress record. An attacker who registers an email address before its owner keeps the password set at registration after the link is made, because sociallogin.connect() does not reset the password and does not disable the existing account, and the owner who later signs in with Google or GitHub is logged into the attacker's account. django-allauth makes the same email based link on its own, because SOCIALACCOUNT_EMAIL_AUTHENTICATION and SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT are both set to True in the same settings module. This gives the attacker the cloud provider credentials, scan findings and tenant memberships held in the account.
- Record
CVE-2026-100169- CVSS 4.0
- 7.6 High
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSVCVE-2026-59151
9.6
Prowler: SAML Domain Claiming Enables Cross-Tenant Account Takeover
Prowler is a cloud security platform. Prior to 5.30.3, Prowler's SAML authentication flow trusted the email domain asserted in a SAMLResponse when deciding which tenant should receive the final token, and the ACS finish logic in api/src/backend/api/v1/views.py recalculated the tenant from user.email instead of binding token issuance to the validated SAML configuration. An authenticated attacker with a controlled SAML IdP could complete a valid SAML flow for an attacker-controlled domain while asserting an email address from another configured domain, causing a SAMLToken and tenant-scoped JWT to be issued for the wrong tenant and enabling cross-tenant account takeover. This issue is fixed in version 5.30.3.
- Record
CVE-2026-59151- CVSS 3.1
- 9.6 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSVGHSA-h8m9-jgf8-vwvp
9.6
Prowler: SAML Domain Claiming Enables Cross-Tenant Account Takeover
SAML Tenant Binding Enables Cross-Tenant Account Takeover
Summary
Prowler's SAML authentication flow trusted the email domain asserted in a SAMLResponse when deciding which tenant should receive the final token. A malicious tenant with its own SAML configuration and a self-controlled IdP could complete a valid SAML flow for its own configured domain, while asserting an email address from another configured domain.
In the vulnerable flow, the ACS finish logic later derived the tenant from the asserted email domain instead of binding token issuance to the tenant associated with the validated SAML configuration. This could cause a token to be issued for the wrong tenant.
The attacker does not generally need to claim the victim's email domain. If the victim tenant already has SAML configured for that domain, another tenant cannot claim it because SAMLConfiguration.email_domain and SAMLDomainIndex.email_domain are globally unique.
Details
The confirmed root cause is in the SAML ACS finish and token issuance flow. The flow selected a SAML configuration through the ACS route, but later recalculated the tenant from the asserted user email domain:
email_domain = user.email.split("@")[-1]
tenant = (
SAMLConfiguration.objects.using(MainRouter.admin_db)
.get(email_domain=email_domain)
.tenant
)
This is unsafe because user.email is derived from the SAML assertion. The tenant used for membership updates and token issuance must come from the SAML configuration validated for the current ACS route, not from the asserted email domain.
The attack is made possible by several compounding weaknesses:
-
No domain ownership proof (
api/src/backend/api/models.py:2100, 2130-2152):SAMLConfiguration.email_domainis validated for format and global uniqueness, but not for domain ownership. Any authenticated tenant admin can claim an unclaimed domain string, but cannot claim a domain already configured by another tenant. -
Global SAML domain index (
api/src/backend/api/models.py:2200-2201):SAMLDomainIndex.update_or_create(email_domain=self.email_domain, defaults={'tenant': self.tenant})maps each configured domain to its tenant. If token issuance later trusts the asserted email domain, it can resolve a tenant different from the one selected by the ACS route. -
Hardcoded auto-connect (
api/src/backend/config/settings/social_login.py:23, 25):SOCIALACCOUNT_EMAIL_AUTHENTICATION = TrueandSOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = Trueare hardcoded and cannot be disabled at runtime. -
IdP-initiated SSO enabled (
api/src/backend/config/settings/social_login.py:78):reject_idp_initiated_sso: Falseallows the attacker to initiate the flow without requiring any action from the victim. -
Token issuance for the wrong tenant (
api/src/backend/api/v1/views.py:853-873): after SAML authentication, the vulnerable ACS finish flow could create membership and issue aSAMLTokenusing a tenant derived from the asserted email domain instead of the validated SAML configuration. -
Token switch impact (
api/src/backend/api/v1/serializers.py:272): the token switch endpoint checks that the authenticated user is a member of the target tenant. If the attacker obtains a JWT for the victim user, they can switch into tenants where that user is already a member.
PoC
Environment setup:
# Build the PoC Docker image (build context = repo root)
docker build -t vuln001-poc -f vuln-001/Dockerfile .
# Start the stack (PostgreSQL + PoC runner)
docker compose -f vuln-001/docker-compose-poc.yml up --no-build --abort-on-container-exit
Automated test (runs inside the container):
python -m pytest poc_vuln001.py -v -s --no-header --tb=short
Manual HTTP exploitation chain (against a live Prowler API):
Step 1 - Attacker configures SAML for their own email domain:
curl -i -X POST "$API/api/v1/saml-config" \
-H "Authorization:…
- Record
GHSA-h8m9-jgf8-vwvp- CVSS 3.1
- 9.6 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Weakness
- CWE-287
- Updated
- 2026-11-02
OSVPYSEC-2026-3725
9.6
Prowler is a cloud security platform. Prior to 5.30.3, Prowler's SAML authentication flow trusted the email domain asserted in a SAMLResponse when deciding which tenant should receive the final token, and the ACS finish logic in api/src/backend/api/v1/views.py recalculated the tenant from user.email instead of binding token issuance to the validated SAML configuration. An authenticated attacker with a controlled SAML IdP could complete a valid SAML flow for an attacker-controlled domain while asserting an email address from another configured domain, causing a SAMLToken and tenant-scoped JWT to be issued for the wrong tenant and enabling cross-tenant account takeover. This issue is fixed in version 5.30.3.
- Record
PYSEC-2026-3725- CVSS 3.1
- 9.6 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
prowler-cloudPyPI From an advisory covering 2 CVEs |
before 5.30.3, plus listed versions 0.0.0rc10.0.0rc100.0.0rc110.0.0rc20.0.0rc3 Show 45 moreShow less0.0.0rc40.0.0rc50.0.0rc60.0.0rc70.0.0rc80.0.0rc93.0.03.0.0rc13.0.0rc113.0.0rc123.0.0rc133.0.0rc143.0.0rc153.0.13.0.23.1.03.1.13.1.23.1.33.1.43.10.03.11.03.11.13.11.23.11.33.12.03.12.13.13.03.13.13.14.03.15.03.15.13.15.23.15.33.16.03.16.13.16.103.16.113.16.123.16.133.16.143.16.153.16.173.16.23.16.3 |
5.30.3 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-100169 is a high vulnerability (CVSS 7.6) in Prowler, published July 10, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.85%, the 58th percentile. No source names a fixed version.
References
github.com/prowler-cloud/prowler/commit/0f31f1a3c2daa05b4d77a78f451e3fbfb3fe3411
github.com/prowler-cloud/prowler/pull/12013
github.com/prowler-cloud/prowler/releases/tag/5.35.0
github.com/prowler-cloud/prowler/security/advisories/GHSA-h8m9-jgf8-vwvp
secur0.com/en/cna/cve-list/cve-2026-100169-prowler-account-pre-hijacking
nvd.nist.gov/vuln/detail/CVE-2026-100169
Show 10 moreShow less
github.com/advisories/GHSA-5qw5-54v9-9g7q
github.com/CVEProject/cvelistV5/tree/main/cves/2026/100xxx/CVE-2026-100169.json
github.com/prowler-cloud/prowler
github.com/prowler-cloud/prowler/releases/tag/5.30.3
github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59151.json
nvd.nist.gov/vuln/detail/CVE-2026-59151
github.com/prowler-cloud/prowler/commit/bf3b5c2ba713e533014927141b64948c82c8f32e
github.com/prowler-cloud/prowler/commit/f5ff30ad175bd2edf02cd28872653c1cda5867b7
github.com/prowler-cloud/prowler/pull/11650
github.com/pypa/advisory-database/tree/main/vulns/prowler-cloud/PYSEC-2026-3725.yaml
Left out here: 141 versions and the rest of 1 source description. The JSON record has them.