In early 2024, a significant security vulnerability was discovered in Keycloak’s handling of SAML signatures, registered as CVE-2024-8698. If your organization uses Keycloak for identity and access management, especially with SAML-based authentication, it’s crucial to understand what this bug is, how attackers can exploit it, and what you should do to protect your users.
This article walks you through the vulnerability with examples, offers a simple breakdown of the technical issue in the Keycloak code, and provides links to more resources.
What is Keycloak?
Keycloak is a widely-used open-source solution that lets organizations manage user authentication, single sign-on, and federation across their web apps and services. It's popular for integrating identity standards like SAML and OIDC.
What is CVE-2024-8698?
CVE-2024-8698 is a flaw in the XMLSignatureUtil class within Keycloak, specifically in how it validates SAML signatures. The problem? The validation logic assumes the scope of the digital signature (meaning, what part of the XML is "protected") based on *where* the Signature appears in the XML tree, rather than what the Signature’s Reference element actually says.
This misinterpretation can lead to signature confusion—letting attackers craft SAML responses that appear valid even when critical portions are unsigned or altered. This could let a malicious user get unauthorized access, escalate privileges, or outright impersonate another user.
How Does the Flaw Work? (Technical Explanation)
To prove the integrity and authenticity of a SAML assertion or response, an Identity Provider (IdP) signs either:
Just the SAML Assertion element inside the Response
This signature is tied to a specific part of the XML via the <ds:Reference> element, which refers to a certain element by ID. Proper validation means that Keycloak should verify *exactly* what was signed and nothing else.
The Keycloak Bug:
The XMLSignatureUtil.verifySignature method *incorrectly* guesses what was signed based on position—for example, if the <Signature> is a direct child of <Response>, it assumes the whole response is protected; as a child of <Assertion>, just the assertion. But the attacker can manipulate the document so this assumption no longer holds true—the Reference could point *anywhere*.
Expected Good SAML Response
<samlp:Response ID="resp123">
<ds:Signature>
<ds:SignedInfo>
<ds:Reference URI="#resp123"> ... </ds:Reference>
</ds:SignedInfo>
... signature value ...
</ds:Signature>
<saml:Assertion ID="assertion456"> ... </saml:Assertion>
</samlp:Response>
The signature signs the whole <Response> since the Reference’s URI matches its ID.
An attacker can create
<samlp:Response ID="resp123">
<!-- Only Assertion is signed, but Signature placed as sibling! -->
<ds:Signature>
<ds:SignedInfo>
<ds:Reference URI="#assertion456"> ... </ds:Reference>
</ds:SignedInfo>
... signature value for assertion only ...
</ds:Signature>
<saml:Assertion ID="assertion456"> ... attacker-controlled content ... </saml:Assertion>
</samlp:Response>
But Keycloak’s flawed check would trust all content as signed just because <Signature> is not under <Assertion>. The attacker can sneak in any content outside the signed Assertion.
Exploit Details
Scenario:
Submits this to the vulnerable Keycloak server.
- Keycloak *improperly* trusts the assertion contents and grants access at the level specified by the attacker.
Exploit Proof-of-Concept in Python
This is not a working exploit, just a pseudo-snippet for education.
from lxml import etree
# Simulating a malicious SAML Response
response = etree.Element("samlp:Response", ID="resp123")
# Create malicious assertion
assertion = etree.Element("saml:Assertion", ID="assertion456")
assertion.text = "I'm an admin now!" # attacker-controlled
# Create Signature signing ONLY the assertion
signature = etree.Element("ds:Signature")
signed_info = etree.SubElement(signature, "ds:SignedInfo")
reference = etree.SubElement(signed_info, "ds:Reference", URI="#assertion456")
# Normally, Signature is inside Assertion...
# Instead, we add it directly under Response
response.append(signature)
response.append(assertion)
# Keycloak improperly considers the whole Response as signed!
print(etree.tostring(response, pretty_print=True).decode())
Upgrade Keycloak:
The maintainers patched this issue. Always upgrade to the latest stable release.
Reject unsigned elements:
Double-check your SAML client configuration. Ensure your service requires signatures on the full document—not just assertions.
References & Further Reading
- Keycloak Security Advisory for CVE-2024-8698
- CVE Record for CVE-2024-8698 on NVD
- Understanding SAML Signatures (Section 5)
- XML Signature Wrapping Attacks
Summary
CVE-2024-8698 exposes a critical SAML validation bug in Keycloak: by trusting signature position over what’s actually signed, attackers can easily bypass protections and perform impersonation or privilege escalation. Every Keycloak admin should patch ASAP and review their SAML security settings.
If you found this post useful, consider subscribing for more vulnerability insights explained in plain language! Stay safe.
Timeline
Published on: 09/19/2024 16:15:06 UTC
Last modified on: 09/20/2024 12:30:17 UTC