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:
- Self-hosted Sentry instance with
SENTRY_SINGLE_ORGANIZATION = False(multi-org mode — this is the default) - At least two organizations exist on the instance
- Attacker has an existing account with permission to configure SSO for at least one organization
- 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_ORGANIZATIONis set toTrueif 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
- CVE Entry: CVE-2026-42354
- GitHub Security Advisory (related): GHSA-ggmg-cqg6-j45g — Sentry SAML SSO Improper Authentication
- Sentry Self-Hosted Upgrade Guide: https://develop.sentry.dev/self-hosted/releases/
- DailyCVE Analysis: CVE-2026-42354 — Sentry SAML SSO Account Takeover
- SAML Vulnerability Landscape (2026): SAML's rough quarter — WorkOS
- CWE-287: Improper Authentication
- Sentry Security Advisories: https://github.com/getsentry/sentry/security/advisories