Sep 11, 2026·6 min read·41 visits
An architectural flaw in Prowler's SAML validation allows attackers to bypass tenant isolation and take over arbitrary victim accounts by manipulating the asserted NameID value in their own custom IdP.
A critical authentication bypass and cross-tenant account takeover vulnerability exists in the Prowler cloud security platform due to improper validation of the SAML Assertion Consumer Service (ACS) flow. An authenticated attacker controlling a custom Identity Provider (IdP) can forge assertions targeting arbitrary user identities across distinct tenants, allowing complete unauthorized access to target tenant-scoped resources.
Prowler is an open-source cloud security platform that provides compliance auditing, threat monitoring, and posture management across multi-tenant environments. To support enterprise-level access, Prowler integrates Single Sign-On (SSO) utilizing the Security Assertion Markup Language (SAML) standard. The implementation routes authentication requests through an Assertion Consumer Service (ACS) endpoint designated for each organization.
The vulnerability is classified under CWE-287 (Improper Authentication). In multi-tenant environments, distinct logical organizations rely on strict separation boundaries during the authentication phase. Prowler utilizes Django-based structures to enforce row-level security (RLS) and scope incoming requests to specific tenant IDs using the database routing layer.
The attack surface lies directly in the public-facing ACS endpoint, which processes signed assertions returned from external SAML Identity Providers (IdP). Because the service dynamically maps user context and tenant scope from verified assertions without ensuring identity-to-tenant cryptographic binding, an attacker can cross-contaminate authorization boundaries.
The technical breakdown of the vulnerability reveals a logical decoupling between the cryptographic validation channel and the final authorization mapping. When an authentication flow begins, the user is redirected to the configured IdP, which signs a SAMLResponse and transmits it back to Prowler's ACS handler. The backend verifies the cryptographic signature of the assertion against the certificate uploaded during the configuration of that specific tenant's SAML endpoint.
The vulnerability is introduced after successful cryptographic verification. In the vulnerable model, ProwlerSocialAccountAdapter checks the global database for an existing user account using a global query. If a match is found, the application invokes the auto-connect function, linking the session to the pre-existing user without verifying that the user belongs to the tenant that configured the matching SAML metadata.
Furthermore, the system extracts the email domain dynamically from the validated user profile (user.email.split("@")[-1]) to resolve the destination tenant. Consequently, when the attacker's IdP asserts an email address belonging to a target tenant (e.g., admin@victim.com), Prowler verifies the signature using the attacker's keys, validates the assertion, queries the database globally, finds the victim's profile, and issues a tenant-scoped JSON Web Token (JWT) matching the victim's organization.
In the vulnerable version of api/src/backend/api/adapters.py, the system attempted to auto-connect social profiles based solely on a global query of the email address. The adapter lacked verification to ensure the user identity matched the configuration of the tenant associated with the ACS endpoint routing path.
# VULNERABLE CODE - api/src/backend/api/adapters.py
class ProwlerSocialAccountAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
# Link existing accounts with the same email address
email = sociallogin.account.extra_data.get("email")
if sociallogin.provider.id == "saml":
email = sociallogin.user.email
if email:
existing_user = self.get_user_by_email(email)
if existing_user:
sociallogin.connect(request, existing_user)The patch addresses this structural vulnerability by performing multi-tiered validations. First, the application extracts the organization_slug from Django's URL resolver path and verifies that it strictly matches the domain extracted from the SAML assertion. Second, the adapter confirms that the pre-existing user is an authorized member of the specific tenant associated with the cryptographically validated SAML configuration.
# PATCHED CODE - api/src/backend/api/adapters.py
class ProwlerSocialAccountAdapter(DefaultSocialAccountAdapter):
def pre_social_login(self, request, sociallogin):
email = sociallogin.account.extra_data.get("email")
if sociallogin.provider.id == "saml":
email = sociallogin.user.email
if not email:
return
domain = email.rsplit("@", 1)[-1].lower()
resolver_match = getattr(request, "resolver_match", None)
organization_slug = (
(resolver_match.kwargs or {}).get("organization_slug", "")
if resolver_match
else ""
).lower()
# Enforce that the route domain matches the assertion domain
if organization_slug != domain:
return
try:
saml_config = SAMLConfiguration.objects.using(MainRouter.admin_db).get(
email_domain=domain
)
except SAMLConfiguration.DoesNotExist:
return
existing_user = self.get_user_by_email(email)
# Restrict account connection only to users inside the matching tenant scope
if existing_user and existing_user.is_member_of_tenant(
str(saml_config.tenant_id)
):
sociallogin.connect(request, existing_user)
returnExploitation of this vulnerability requires the attacker to have low privileges, specifically the ability to create and configure a tenant under their own control. The attacker registers a domain (e.g., attacker.com) and creates a custom SAML configuration pointing to an Identity Provider controlled by the attacker.
Using the custom IdP, the attacker constructs a payload that signs a custom SAMLResponse. In this assertion, the attacker modifies the NameID and email attributes to target a legitimate administrative user of a separate tenant (e.g., security-lead@victim.com). The attacker then submits this forged assertion to the ACS endpoint representing their own organization.
Because the signature matches the keys loaded for the attacker's configuration, the signature check succeeds. The backend then extracts the victim's email, matches it globally, dynamically switches the context to victim.com due to the domain parsing logic, and returns a tenant-scoped authorization token. The attacker gains full administrative access to the victim's tenant without possessing credentials or control over the victim's true identity provider.
The impact of this vulnerability is critical, leading to complete unauthorized access and compromise of tenant-scoped information. Because Prowler is used for cloud security posture management, its tenants hold highly sensitive data, including cloud account metadata, compliance reports, vulnerability data, and potentially administrative access keys to integrated cloud environments.
The CVSS v3.1 score is calculated at 9.6. The attack vector is Network (AV:N), the complexity is Low (AC:L), and the privileges required are Low (PR:L), as registration of a malicious tenant is a prerequisite. The Scope is Changed (S:C) because compromising the identity within the attacker's domain allows crossing logical tenant boundaries to manipulate and view information in the victim's domain.
While there is currently no evidence of weaponized or active in-the-wild exploitation, the severity of the flaw makes it an attractive target. A successful compromise completely undermines the row-level security boundaries of the platform, resulting in high confidentiality and integrity losses.
The primary remediation strategy is upgrading Prowler deployments to version 5.30.3 or higher. If running the platform via containerized deployments, administrators must update both the frontend and API backend images to pull tags corresponding to the fixed release. No configuration changes are required after applying the patch, as the corrected code handles domain verification dynamically.
To detect potential exploitation attempts in historical logs, security teams should analyze web server access logs and application server events. Look for instances where SAML assertion flows target ACS endpoints where the host or URI organization slug does not match the domain of the asserted email addresses.
Additionally, review application logs for the presence of the error string: "SAML email domain does not match requested organization". The occurrence of this message indicates that the security controls introduced in the patch successfully intercepted an invalid domain assertion attempt, signaling potential malicious testing or active exploitation.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Prowler Prowler Cloud | < 5.30.3 | 5.30.3 |
Prowler API Prowler Cloud | < 1.31.3 | 1.31.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 | 9.6 |
| EPSS Score | 0.00322 |
| Impact | Cross-Tenant Account Takeover |
| Exploit Status | None (No active public exploits) |
| KEV Status | Not Listed |
The application fails to restrict account linking and session initialization to the domain associated with the validated cryptographic signature, permitting cross-tenant authorization.
CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.
An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.
CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.
CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.
CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.
CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.