
Security Assertion Markup Language (SAML) is an open standard for exchanging authentication and authorization data between parties in a federated identity system. It enables single sign-on (SSO) across domains. The key components are:
In a typical SAML flow, the SP redirects the user to the IdP for authentication. The IdP returns a signed SAML response containing assertions about the user (e.g., email, name). The SP validates the signature and provisions or logs in the user.
CVE-2021-21239 is a signature validation bypass vulnerability in the pysaml2 library (versions <6.5.0), used by Redash <=10.1.0 for SAML SSO. At a high level, it allows an attacker to forge a SAML response with an arbitrary embedded public key, enabling user impersonation and privilege escalation via just-in-time (JIT) provisioning.
The vulnerability stems from how pysaml2 invokes xmlsec1 to verify SAML signatures. Xmlsec1, by default, prefers embedded public keys in the SAML response's <ds:KeyInfo> over the configured IdP certificate. An attacker can craft a SAML response with a self-generated RSA key pair, embed the public key in <ds:RSAKeyValue>, and sign the assertion with the private key. Since pysaml2 does not restrict xmlsec1 to use only the trusted IdP certificate, the forged signature is deemed valid, allowing the attacker to log in as any user (e.g., admin) and gain highest privileges.
At the code level, the vulnerability exists in pysaml2's sigver.py (check_signature function). The xmlsec1 command list lacks the --enabled-key-data raw-x509-cert option, allowing xmlsec1 to use untrusted embedded keys:
com_list = [
self.xmlsec, '--verify', '--enabled-reference-uris', 'empty,same-doc',
'--pubkey-cert-pem', cert_file_name,
'--id-attr:%s' % id_attr, node_name,
'--node-id', nodeid, '--output', output_file_name, xml_file_name
]
This permits attacks where the attacker modifies the email in <saml:NameID> to impersonate any user, leveraging JIT provisioning.
To test the exploit, a local Redash instance (v10.1.0 or earlier) is spun up using Docker Compose. The setup involves configuring SAML SSO with Google's IdP:
This setup allows testing the forged SAML response sent via a Python script or Burp Suite, verifying privilege escalation.
The vulnerability arises because pysaml2's xmlsec1 invocation allows embedded keys to be used for verification, bypassing the trusted IdP certificate. An attacker generates an RSA key pair, crafts a SAML response with the public key in <ds:RSAKeyValue>, modifies attributes (e.g., email to admin's), and signs the assertion. Xmlsec1 verifies using the embedded key, deeming it valid.
The fix was implemented in pysaml2 v6.5.0 (initial commit on Jan 17, 2021) in src/saml2/sigver.py. The xmlsec1 command list was updated to include --enabled-key-data raw-x509-cert, restricting verification to the configured IdP certificate:
com_list = [
self.xmlsec, '--verify', '--enabled-reference-uris', 'empty,same-doc',
'--pubkey-cert-pem', cert_file_name,
'--enabled-key-data', 'raw-x509-cert',
'--id-attr:%s' % id_attr, node_name,
'--node-id', nodeid, '--output', output_file_name, xml_file_name
]
This enforces trust chain validation, preventing embedded key usage. Redash fixed this by upgrading pysaml2 in later versions (e.g., v11+). Vulnerable versions did not use --insecure; the bug was the missing restriction option.
The exploit code is implemented in the cve-2021-21239.py. This Python script automates the process of forging a SAML response to exploit CVE-2021-21239 in Redash <=10.1.0, enabling user impersonation and privilege escalation via JIT provisioning. The exploit targets Google Workspace SAML IdP but the attack has been tested with other IdPs (keycloak). The script performs the following steps:
email="[email protected]").idpid from Redash’s SAML login endpoint if not provided, ensuring compatibility with the target’s configuration.SAMLResponseTempalte.xml) with placeholders for email, names, timestamps, and assertion ID. Dynamic UTC timestamps are generated with a 5-minute validity window to pass Redash’s validation.xmlsec Python library to sign the <saml:Assertion> element with RSA-SHA256, embedding the public key in <ds:RSAKeyValue> to exploit the vulnerability./saml/callback?org_slug=default).Exploiting CVE-2021-21239 required crafting a forged SAML response that Redash accepts via JIT provisioning. Key difficulties and bypasses:
<ds:RSAKeyValue>, signing with the private key.xmlsec1 binary worked, but Python xmlsec library caused digest mismatches due to whitespace/canonicalization issues. Bypassed by serializing XML to a single line (etree.tostring(..., pretty_print=False)) before signing.NotOnOrAfter caused rejection; bypassed by using dynamic UTC timestamps with 5-minute validity.SAMLResponse, causing parsing failures; bypassed by sending raw form data.These challenges highlight the need for precise XML manipulation and environment setup to exploit the vulnerability effectively. The provided exploit code successfully demonstrates this, forging a SAML response to impersonate users and gain access.