Vulnerability Analysis

CVE-2026-42354: Sentry SAML SSO Authentication Bypass Enables Full Account Takeover

Executive Summary

CVE-2026-42354 is a critical-severity (CVSS 9.1) improper authentication vulnerability in Sentry's SAML Single Sign-On implementation that allows an attacker to take over any user account on a vulnerable self-hosted Sentry instance without knowing the victim's password. The flaw arises from insufficient cross-organization validation of SAML Identity Provider assertions, letting a malicious actor forge identity claims that Sentry blindly trusts. A patch is available in version 26.4.1 and all self-hosted operators should upgrade immediately. This vulnerability drops during a notably rough period for SAML-based authentication, where at least five critical SAML flaws were disclosed across major platforms in early 2026.


1. What Is This Vulnerability?

Sentry is the world's most popular open-source error tracking and performance monitoring platform, with tens of thousands of self-hosted deployments at enterprises, startups, and government agencies worldwide. Its SAML SSO feature allows teams to authenticate using a corporate Identity Provider (IdP) such as Okta, Azure AD, or a self-managed IdP.

The vulnerability lives in how Sentry validates SAML assertions during the SSO login flow. When a user authenticates through SAML, Sentry receives an XML assertion from the configured IdP. The assertion contains a <NameID> element — typically the user's email address — which Sentry uses to look up and link the authenticating user to an internal account.

The critical flaw: Sentry failed to enforce that a SAML assertion must originate from the IdP configured for the same organization as the target user's account. In a multi-organization Sentry instance, an attacker who controls an organization and can configure its SSO settings could register a malicious IdP for their own organization, then craft a SAML assertion claiming the email address of a victim user in any other organization. Sentry would accept the assertion, link the attacker's session to the victim's account, and grant full access — no password required.

How the Authentication Flow Is Exploited

In a normal SAML authentication flow:

User → Sentry Login → Redirect to IdP → IdP authenticates user
     → IdP sends signed SAML assertion → Sentry validates assertion
     → Sentry links assertion email to user account → Session granted

In the exploited flow:

Attacker (controls Org B, configures malicious IdP)
  → Sends crafted SAML assertion to: /api/0/saml-acs/org-b/
    containing NameID = victim@target-org.com
  → Sentry checks: "Is this assertion from Org B's IdP?" YES
  → Sentry checks: "Is victim@target-org.com a valid user?" YES
  → ❌ Sentry FAILS to check: "Does this IdP belong to victim's org?"
  → Sentry grants attacker full access to victim's account

The missing validation is the cross-organization IdP ownership check. Sentry's SAML assertion consumer service (ACS) endpoint accepted assertions for any user email regardless of which organization's IdP issued the assertion.

Attack Vector

  • Attack Vector: Network (remotely exploitable)
  • Attack Complexity: Low
  • Privileges Required: The attacker must have an existing account in the Sentry instance with permission to modify SSO settings for any organization (not necessarily an admin of the target org)
  • User Interaction: None required from the victim
  • Scope: High Confidentiality, High Integrity impact

Real-World Impact

An attacker exploiting CVE-2026-42354 gains full control over the victim's Sentry account, including:

  • Access to all error logs, stack traces, and source maps — which often contain API keys, internal URLs, PII, and secrets inadvertently captured in exceptions
  • Control of alerts, integrations, and webhook configurations — enabling lateral movement to connected services (Slack, PagerDuty, GitHub)
  • Ability to modify alert rules and silence monitoring — facilitating cover for ongoing attacks
  • Access to session replay data — which can expose customer PII and application behavior

Because Sentry aggregates some of the most sensitive diagnostic data in an organization's environment, a Sentry account takeover can function as a pivot point into much broader infrastructure.


2. Who Is Affected?

Vulnerable configurations:

Condition Vulnerable?
Sentry SaaS (sentry.io) ✅ Patched (fix deployed Feb 18, 2026)
Self-hosted Sentry 21.12.0 – 26.4.0, multi-org mode ✅ Vulnerable
Self-hosted Sentry with SENTRY_SINGLE_ORGANIZATION=True ❌ Not vulnerable
Self-hosted Sentry 26.4.1 or later ❌ Not vulnerable

Conditions required for exploitation:

  1. Self-hosted Sentry instance with SENTRY_SINGLE_ORGANIZATION = False (multi-org mode — this is the default)
  2. At least two organizations exist on the instance
  3. Attacker has an existing account with permission to configure SSO for at least one organization
  4. Attacker knows a victim's email address (often trivial via LinkedIn, company directory, or Gravatar enumeration)

Who is most at risk: Enterprises running self-hosted Sentry for multiple teams or business units, SaaS companies that white-label or share a Sentry instance across customers, and managed service providers running Sentry for multiple clients.


3. How to Detect It (Testing)

Manual Testing Steps

Step 1: Check your Sentry version

# For Docker-based self-hosted installations
docker exec sentry-web sentry --version

# For source installations
python -m sentry --version

If the version is between 21.12.0 and 26.4.0 (inclusive), proceed to Step 2.

Step 2: Check multi-organization mode

# Inspect your config.yml or sentry.conf.py
grep -i "SENTRY_SINGLE_ORGANIZATION" /etc/sentry/sentry.conf.py /etc/sentry/config.yml 2>/dev/null

If the output shows False or if the setting is absent (defaults to False), your instance supports multiple organizations and is potentially vulnerable.

Step 3: Check organization count

# Via Sentry management command
docker exec sentry-web sentry exec "from sentry.models import Organization; print(Organization.objects.count())"

If the count is greater than 1, the attack prerequisites are met.

Step 4: Review SSO configurations across organizations

Log into Sentry as a superuser, navigate to Admin → Organizations, and for each organization review Settings → Auth → SSO Providers. If any organization has a third-party SSO configured, evaluate whether all SSO operators are trusted.

Step 5: Check SAML ACS endpoint accessibility

# Verify the ACS endpoints are reachable
curl -I https://your-sentry-instance.com/api/0/saml-acs/<your-org-slug>/

A 405 Method Not Allowed (GET not allowed) response indicates the endpoint is present and reachable.

Automated Scanning

Using Nuclei (recommended):

# Install Nuclei if needed
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

# Run Nuclei against your Sentry instance for authentication issues
nuclei -u https://your-sentry-instance.com \
  -tags saml,auth,sso \
  -severity critical,high \
  -o sentry-saml-scan.txt

Using a custom check script:

#!/usr/bin/env python3
"""
CVE-2026-42354 Sentry Version Checker
Checks if a self-hosted Sentry instance is running a vulnerable version.
"""
import requests
import sys

def check_sentry_version(base_url):
    try:
        # Sentry exposes version info at /_health/ endpoint
        health_url = f"{base_url.rstrip('/')}/_health/"
        resp = requests.get(health_url, timeout=10, allow_redirects=True)
        
        # Check API endpoint for version info
        api_url = f"{base_url.rstrip('/')}/api/0/"
        api_resp = requests.get(api_url, timeout=10)
        
        if api_resp.status_code == 200:
            data = api_resp.json()
            version = data.get("version", {}).get("current", "unknown")
            print(f"[*] Sentry version detected: {version}")
            
            # Parse and compare version
            try:
                parts = version.split(".")
                major, minor = int(parts[0]), int(parts[1])
                # Affected: 21.12.0 - 26.4.0
                if (major == 21 and minor >= 12) or (22 <= major <= 25) or (major == 26 and minor < 4):
                    print(f"[!] VULNERABLE: Version {version} is affected by CVE-2026-42354")
                    return True
                elif major == 26 and minor >= 4:
                    print(f"[+] SAFE: Version {version} is patched (>= 26.4.1 required)")
                else:
                    print(f"[?] UNKNOWN: Cannot determine vulnerability status for {version}")
            except (ValueError, IndexError):
                print(f"[?] Could not parse version: {version}")
        else:
            print(f"[?] Could not retrieve API info (status: {api_resp.status_code})")
            
    except requests.RequestException as e:
        print(f"[-] Connection error: {e}")
    
    return False

if __name__ == "__main__":
    target = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:9000"
    check_sentry_version(target)

Code Review Checklist

For security engineers reviewing Sentry configurations or custom SAML integrations:

  • Confirm Sentry version is 26.4.1 or higher (sentry --version)
  • Verify SENTRY_SINGLE_ORGANIZATION is set to True if only one org is needed
  • Audit which users have SSO configuration permissions across all organizations
  • Review SAML ACS endpoint logs for cross-organization assertion attempts
  • Confirm 2FA is enforced at the user-account level (not just SSO)
  • Check for anomalous login sessions in Admin → Users (unusual IPs, login times)

4. How to Fix It (Mitigation)

Step-by-Step Remediation

The primary fix is upgrading to Sentry 26.4.1, which introduces explicit cross-organization validation of SAML IdP origin before processing assertions.

For Docker-based self-hosted installations (most common):

# 1. Pull the patched image
docker pull getsentry/sentry:26.4.1

# 2. Backup your database before proceeding
docker exec sentry-postgres pg_dump -U sentry sentry > sentry-backup-$(date +%Y%m%d).sql

# 3. Stop running services
cd /path/to/self-hosted
docker-compose down

# 4. Update the image version in docker-compose.yml
# Change: image: getsentry/sentry:26.x.x
# To:     image: getsentry/sentry:26.4.1
sed -i 's/image: getsentry\/sentry:.*/image: getsentry\/sentry:26.4.1/' docker-compose.yml
sed -i 's/image: getsentry\/snuba:.*/image: getsentry\/snuba:26.4.1/' docker-compose.yml

# 5. Run migrations
docker-compose run --rm web upgrade --noinput

# 6. Start services
docker-compose up -d

# 7. Verify the upgrade
docker exec sentry-web sentry --version

For pip/source installations:

# 1. Activate your virtual environment
source /path/to/sentry-venv/bin/activate

# 2. Upgrade Sentry
pip install "sentry==26.4.1"

# 3. Run database migrations
sentry upgrade --noinput

# 4. Restart the web and worker processes
supervisorctl restart sentry-web sentry-worker sentry-cron
# or if using systemd:
systemctl restart sentry-web sentry-worker sentry-cron

# 5. Verify
sentry --version

Interim Workaround (If Immediate Upgrade Is Not Possible)

If you cannot immediately upgrade, these workarounds reduce exposure:

Option A: Enable single-organization mode

# In sentry.conf.py — forces single-org mode, disables multi-org features
SENTRY_SINGLE_ORGANIZATION = True

Restart Sentry after applying. Note: this disables the ability to have multiple organizations.

Option B: Restrict SSO configuration permissions

Remove SSO configuration access from any user who should not have it. In Sentry, SSO settings can only be modified by organization owners. Audit and demote users as needed via Settings → Members → [User] → Role.

Option C: Require 2FA for all accounts

While this does not prevent the account linking itself, it forces the attacker to complete a second factor before accessing the linked session, providing a detection opportunity.

# Send 2FA reminder emails to all users via Sentry CLI
docker exec sentry-web sentry exec "
from sentry.models import User
for user in User.objects.filter(is_active=True):
    if not user.has_usable_password():
        continue
    try:
        user.send_2fa_reminder_email()
    except Exception as e:
        print(f'Error for {user.email}: {e}')
"

Code Fix Example

The underlying fix Sentry implemented validates that the IdP issuing the SAML assertion is the same IdP registered for the organization that owns the target user account:

Before (vulnerable — simplified):

def process_saml_response(org_slug, saml_response):
    org = Organization.objects.get(slug=org_slug)
    idp = SSOProvider.objects.get(organization=org)
    
    # Validate assertion is signed by THIS org's IdP
    assertion = validate_saml_signature(saml_response, idp.public_key)
    user_email = assertion.get_subject_email()
    
    # ❌ VULNERABLE: Looks up user by email globally, not scoped to org
    user = User.objects.get(email=user_email)
    return create_session(user)

After (patched — simplified):

def process_saml_response(org_slug, saml_response):
    org = Organization.objects.get(slug=org_slug)
    idp = SSOProvider.objects.get(organization=org)
    
    # Validate assertion is signed by THIS org's IdP
    assertion = validate_saml_signature(saml_response, idp.public_key)
    user_email = assertion.get_subject_email()
    
    # ✅ PATCHED: User must belong to the SAME organization as the IdP
    member = OrganizationMember.objects.get(
        organization=org,
        email=user_email
    )
    user = member.user
    return create_session(user)

Configuration Hardening

# sentry.conf.py — recommended security settings

# If you only need one organization, lock it down
SENTRY_SINGLE_ORGANIZATION = True  # Eliminates cross-org attack surface

# Enforce 2FA at the application level
SENTRY_FEATURES['auth:register'] = False  # Disable public registration

# Restrict SSO to specific IP ranges if your IdP has known IPs
# (configure via your reverse proxy / load balancer)

5. How to Test the Fix (Validation)

Regression Test Scenarios

After upgrading to 26.4.1, verify the fix is effective with the following tests:

Scenario A: Verify that legitimate cross-org SAML authentication still works correctly

  • Log into Org A using the configured IdP for Org A → Should succeed normally

Scenario B: Verify the vulnerability no longer exists

  • Create a test organization (Org B) with a custom SAML IdP
  • Attempt to submit a SAML assertion for Org B's ACS endpoint that claims a user email from Org A
  • Expected: Sentry returns a 401 or redirects to an error page. The user from Org A is NOT authenticated.

Scenario C: Verify single-org mode fully blocks the attack surface

  • With SENTRY_SINGLE_ORGANIZATION = True, attempt to access any org-switching endpoints
  • Expected: Multi-org features are disabled; no alternate ACS endpoints exposed

Security Test Cases

Test Case 1: Cross-organization SAML assertion injection

import requests
import base64
from datetime import datetime, timedelta

# Target: a Sentry instance running 26.4.1+
TARGET_SENTRY = "https://your-sentry-instance.com"
ATTACKER_ORG = "attacker-org-slug"    # Org the attacker controls
VICTIM_EMAIL = "victim@target-org.com"  # Email of user in a different org

# Craft a minimal SAML assertion (unsigned, for testing purposes only)
now = datetime.utcnow()
not_after = now + timedelta(hours=1)

saml_assertion = f"""<?xml version="1.0"?>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
                xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
  <saml:Assertion>
    <saml:Subject>
      <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
        {VICTIM_EMAIL}
      </saml:NameID>
    </saml:Subject>
    <saml:Conditions NotBefore="{now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
                     NotOnOrAfter="{not_after.strftime('%Y-%m-%dT%H:%M:%SZ')}"/>
    <saml:AttributeStatement>
      <saml:Attribute Name="email">
        <saml:AttributeValue>{VICTIM_EMAIL}</saml:AttributeValue>
      </saml:Attribute>
    </saml:AttributeStatement>
  </saml:Assertion>
</samlp:Response>"""

encoded_response = base64.b64encode(saml_assertion.encode()).decode()

# Attempt to exploit
resp = requests.post(
    f"{TARGET_SENTRY}/api/0/saml-acs/{ATTACKER_ORG}/",
    data={"SAMLResponse": encoded_response, "RelayState": ""},
    allow_redirects=False
)

print(f"Response status: {resp.status_code}")
if resp.status_code == 302 and "sentry" in resp.headers.get("Location", ""):
    print("❌ VULNERABLE: Attacker was redirected to authenticated session")
elif resp.status_code in (401, 403, 400):
    print("✅ PATCHED: Server correctly rejected the cross-org assertion")
else:
    print(f"⚠️  Unexpected response — manual investigation required")
    print(f"Location: {resp.headers.get('Location', 'N/A')}")

Test Case 2: Verify 2FA blocks post-linkage access

  • Even on a patched instance, confirm that accounts with 2FA enabled require the second factor after any SSO authentication attempt

Test Case 3: Audit log review

  • Navigate to Admin → Audit Log and filter for SAML or SSO events
  • Confirm no cross-organization authentication events appear in the logs

Automated Tests

#!/bin/bash
# Quick post-patch validation script

SENTRY_URL="https://your-sentry-instance.com"
SENTRY_TOKEN="your-superuser-api-token"

echo "=== CVE-2026-42354 Post-Patch Validation ==="

# 1. Confirm version
echo -n "[1] Checking Sentry version... "
VERSION=$(curl -s -H "Authorization: Bearer $SENTRY_TOKEN" \
  "$SENTRY_URL/api/0/" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('version',{}).get('current','unknown'))")
echo "$VERSION"

if python3 -c "
v='$VERSION'.split('.')
major,minor=int(v[0]),int(v[1])
exit(0 if (major > 26 or (major == 26 and minor >= 4)) else 1)
"; then
  echo "    ✅ Version is patched (>= 26.4.x)"
else
  echo "    ❌ Version is VULNERABLE — upgrade required"
fi

# 2. Check organization count
echo -n "[2] Checking organization count... "
ORG_COUNT=$(curl -s -H "Authorization: Bearer $SENTRY_TOKEN" \
  "$SENTRY_URL/api/0/organizations/?member=0" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))")
echo "$ORG_COUNT organization(s)"

# 3. Check single-org mode
echo -n "[3] Checking single-organization mode... "
if grep -q "SENTRY_SINGLE_ORGANIZATION.*True" /etc/sentry/sentry.conf.py 2>/dev/null; then
  echo "✅ Enabled (attack surface minimized)"
else
  echo "⚠️  Disabled (multi-org mode, ensure patch is applied)"
fi

echo ""
echo "=== Validation Complete ==="

6. Prevention & Hardening

Best Practices

1. Enforce the principle of least privilege for SSO configuration

Not every organization administrator needs the ability to configure SSO providers. Regularly audit who has owner-level access across all organizations in your Sentry instance. Treat SSO configuration as a high-privilege operation equivalent to modifying authentication policy.

2. Maintain a rigorous patch cadence for Sentry

Sentry releases security patches frequently. Subscribe to the getsentry/sentry GitHub Security Advisories feed and establish an internal SLA (e.g., critical patches deployed within 72 hours) for your self-hosted instance.

3. Mandate 2FA for all Sentry accounts

Two-factor authentication cannot prevent the account-linking phase of this attack, but it adds a meaningful layer of defense that can block account takeover even if the SAML assertion is accepted. Enforce 2FA in user settings and send periodic reminders to non-compliant accounts.

4. Prefer SENTRY_SINGLE_ORGANIZATION = True where possible

If your organization only uses Sentry for a single team or business unit, enabling single-organization mode eliminates the entire cross-organization attack surface class. This is the most effective architectural control for most self-hosted deployments.

5. Use OIDC/OAuth 2.0 SSO instead of SAML where available

Sentry supports OAuth2-based SSO providers (Google, GitHub, Azure AD via OIDC). The OIDC protocol has a significantly simpler security model than SAML's XML-signature-based approach and avoids the multi-parser attack surface that produces most SAML CVEs. For new integrations, prefer OIDC.

Monitoring & Detection

1. Monitor Sentry's authentication audit log

Sentry's admin interface exposes an audit log (/manage/audit-log/) that records SSO configuration changes and login events. Set up alerts for:

  • Any new SSO provider being added or modified
  • Authentication events from unexpected IP addresses
  • Users logging in via a different organization's SSO than their own

2. Enable SIEM integration for Sentry logs

Configure Sentry to forward access logs to your SIEM (Splunk, Elastic, Datadog, etc.). Create detection rules for:

# Example Splunk detection rule for anomalous SAML authentications
index=sentry_logs sourcetype=sentry_access
| where uri_path matches "/api/0/saml-acs/*"
| stats count by src_ip, org_slug, user_email
| where count > 3
| alert if different org_slugs share the same user_email within 5min

3. Watch for signs of post-compromise activity

If an account takeover occurred before patching, look for:

  • Unusual integrations created or modified (webhooks pointing to unknown URLs)
  • Alert rules silenced or deleted around the time of compromise
  • API tokens generated by accounts that don't normally create them
  • Source map downloads or project access from unusual geographies

4. Implement network-level controls on the SAML ACS endpoint

If your IdP has known IP ranges, configure your reverse proxy or WAF to only accept SAML ACS requests from those IPs:

# nginx example — restrict SAML ACS to known IdP IPs
location ~ ^/api/0/saml-acs/ {
    allow 203.0.113.0/24;   # Your IdP IP range
    allow 198.51.100.10;    # Your IdP secondary IP
    deny all;
    proxy_pass http://sentry-backend;
}

References

Latest from the blog

See all →