Keycloak CVE-2026-103884: X.509 CRL Path Traversal When CRL Checking Is On

Guilliano Molaire Guilliano Molaire 12 min read

CVE-2026-103884 affects Keycloak realms that use certificate-based authentication (X.509 client certificates) with the option to read revocation information from the certificate’s CRL Distribution Point turned on. In that mode Keycloak takes a location out of the presented certificate, and if that location is not an http or ldap URL it treats it as a file path under its conf directory without checking that the path stays inside that directory, so a crafted certificate containing ../ can make the server open files elsewhere on its disk. The advisory describes the possible outcomes as unauthorized file access and memory exhaustion from loading a very large file. At the time of writing I could not find the fix on main or on any 26.x release tag I inspected, so the practical response today is to check whether you use that option and, if you do, to reduce your exposure with the interim controls below.

The rest of this post explains what X.509 authentication and CRL Distribution Points do in Keycloak, what the code does wrong, who is realistically in scope, how to check your own realms, and what to do until a patched release ships. It is written for engineers who run Keycloak themselves or who use it as a managed service. The flaw description comes from the GitHub advisory and the Keycloak repository, and I mark the places where I am inferring behaviour from the source rather than quoting a source.

What is CVE-2026-103884?

It is a path traversal weakness in the X.509 client certificate authenticator of Keycloak. It is tracked as CVE-2026-103884 and listed in the GitHub advisory database as GHSA-xfh5-8fx4-3crm, whose description says that when CRL Distribution Point checking is enabled the server fails to properly validate file paths provided in a client certificate, which can lead to unauthorized file access or to memory exhaustion from loading excessively large files. I could not confirm the advisory’s severity rating independently, so check the advisory itself for the current score and for any affected and patched version ranges it lists.

Keycloak’s own tracking is in issue #50600. Upstream there is a pull request, #50601, proposing a normalize-and-containment fix, which resolves the path and rejects anything that lands outside the configuration directory.

What do certificate-based authentication and CRL Distribution Points do in Keycloak?

X.509 client certificate authentication lets a user, or a device acting for a user, prove who they are during login by presenting a certificate during the TLS handshake instead of typing a password. Keycloak’s server administration guide describes the authenticator as extracting the user identity from the certificate (for example from the subject, an email address or a serial number), mapping it to a Keycloak user and optionally validating the certificate further before it signs the user in. The same mechanism shows up in the Keycloak security audit and hardening checklist as one of the strong authentication options worth considering, and it is common in government, defense, healthcare and large enterprise deployments where smart cards or device certificates are already issued.

Certificate validation includes a revocation check, because a certificate can be valid by date and signature and still have been withdrawn because a card was lost or an employee left. A certificate revocation list, or CRL, is a signed file published by the certificate authority that lists the serial numbers of certificates it has revoked. A CRL Distribution Point, or CDP, is an extension inside the certificate itself that tells a relying party where to fetch the relevant CRL. Most public key infrastructure products put a CDP in every certificate they issue, and Keycloak’s admin guide says as much.

The authenticator therefore offers two ways to find the CRL, and this CVE concerns only the second. In the first, an administrator types the location into the authenticator configuration and Keycloak uses that fixed value for every login. In the second, the administrator ticks a box and Keycloak reads the location out of each certificate that is presented, so the value is chosen by whoever issued or holds the certificate and not by the administrator.

What exactly goes wrong in the code?

When Keycloak has a CRL location it decides how to load it from the start of the string. The loader in CertificateValidator.java treats values starting with http as a remote download, values starting with ldap as a directory lookup, and everything else as a file path relative to the server’s configuration directory. That configuration directory is read from the jboss.server.config.dir system property. The name looks like a leftover from an older application server, but I checked the current source and it is still the live mechanism: the kc.sh and kc.bat launch scripts in the Quarkus distribution set it to the conf directory, so on a standard Keycloak 26.x install it points at conf/.

The file branch builds the path by joining the two strings together as configDir + File.separator + relativePath and then checks whether the result is a file. There is no step that normalizes the path (resolving .. segments) and no check that the final location is still under the configuration directory. A CDP value such as ../../../../etc/passwd therefore resolves to a file well outside conf/, and if that file exists and is readable, Keycloak opens it. The check is f.isFile(), which is false for devices such as /dev/zero, so an attacker needs the path of a real, regular file that is large.

When CDP checking is on, the list of locations comes straight from the certificate. The code in checkRevocationStatusUsingCRLDistributionPoints reads the distribution points from the first certificate in the presented chain and creates a loader for each one. That is the step that turns a configuration-directory convenience (relative paths for CRL files an administrator drops into conf/) into an input channel controlled by the certificate holder. The upstream issue notes that Keycloak’s theme resource loader already does the right thing for the same problem, normalizing the path and confirming it stays under the base directory, and the proposed fix in pull request #50601 does the equivalent with Path.normalize() and a startsWith() containment check.

What can an attacker actually do with it?

The advisory describes two outcomes, and from the source I can say a little more about how each one behaves.

Memory exhaustion. After opening the file, the loader hands the stream to the JDK’s X.509 certificate factory to parse it as a CRL. I did not run an exploit, so this is my reading rather than a demonstrated result, but a parser that is pointed at a very large file can be made to read a lot of data into memory, which fits the memory exhaustion the advisory describes. This is the impact I would take most seriously, because it can degrade a shared login service for every user in every realm on that server.

Limited file access. The attacker does not appear to get file contents back. In the code I read, a file that is not a valid CRL fails to parse and the login simply fails, and the upstream issue itself describes the behaviour as opening arbitrary files rather than disclosing their contents. What remains is mostly a probe: whether a given path exists and is readable can influence which error the server logs and possibly how long the attempt takes, which is useful to someone mapping a host and little more. I would not describe this as an arbitrary file read, and I would be wary of any summary that does.

Two further points limit the damage. The traversal only escapes the directory with ../ segments, because an absolute path is simply appended to the configuration directory and stays inside it. And the attacker must get a certificate in front of the authenticator that contains the crafted value.

Who is in scope, and how hard is it to exploit?

You are in scope when all of the following are true: the realm uses an authentication flow with the X.509 authenticator (either the browser flow step or the direct grant variant), the authenticator has revocation checking turned on with the CRL Distribution Point option selected, and the Keycloak process can read files outside conf/, which is nearly always the case.

The remaining question is how an attacker obtains a certificate with a hostile CDP. Reading the source, certificate trust validation runs before the revocation check when it is enabled, so a certificate that must chain to a trusted certificate authority would have to be issued by that authority with the hostile CDP value in it. That would make exploitation harder, and it means the risk is concentrated in deployments where certificates are issued freely or where a chain-of-trust check is not enforced between the client and the authenticator. If you terminate mutual TLS at a reverse proxy and forward the client certificate to Keycloak in a header, review what the proxy validates, because the trust decision may have been made somewhere that does not apply to this field. I am inferring this exposure model from the order of the code, and the advisory does not spell it out.

Your setup Exposure to this CVE
No X.509 authenticator in any flow Not affected
X.509 with CRL checking off, or OCSP only Not affected by this path
X.509 with CRL checking on and a fixed CRL file path or URL you configured Not affected, because the location is not taken from the certificate
X.509 with CRL checking on and the CRL Distribution Point option on In scope: the location comes from the presented certificate
Same, and certificates are issued by a closed internal CA with controlled enrollment In scope, but practically harder to reach
Same, and clients can obtain certificates freely or trust validation is not enforced Highest priority

How do I check whether my realms are exposed?

In the admin console, open the realm, go to Authentication, and look through each flow for an X.509 step (the names are “X509/Validate Username Form” in a browser flow and “X509/Validate Username” in a direct grant flow). Open the step’s settings with the gear icon. The relevant options, named in the admin guide, are:

  • CRL Checking Enabled, which turns on revocation checking by certificate revocation list, with the location defined in the CRL path option.
  • Enable CRL Distribution Point to check certificate revocation status, which makes Keycloak read the location from the certificate. This is the setting that exposes the vulnerable path.
  • CRL Path (the admin guide calls it “CRL file path”), the administrator-supplied location that is used when the distribution point option is off.
  • CRL abort if non updated, which controls whether an outdated CRL fails the login.

If the distribution point option is on in any flow, you are in scope. You can also list authenticator configurations through the admin REST API and filter for the X.509 provider if you have many realms, because the check is just a boolean in each configuration. Do the same for every realm and do not forget realms that are bound only to a client-specific flow override.

What can I do until a fixed release ships?

Check the Keycloak release notes first, because the situation is moving. At the time of writing I could not find the fix on main or on the 26.4, 26.6, 26.7 or 26.8 release tags I inspected (26.4.16, 26.6.7, 26.7.5 and 26.8.0 all contain the unguarded path concatenation), so I treat every 26.x line as affected until the maintainers say otherwise. I did not find a fixed-version statement I could read, and I will not name one here.

Until a patched release exists, these controls reduce the risk, listed from most to least effective:

  1. Switch off the distribution point option where you can. If your CAs publish one CRL, configure its URL or file in the CRL Path option and turn the distribution point option off, so the location no longer comes from the certificate. The trade-off is that you must keep that CRL fresh, and you need one entry per issuing CA.
  2. Use OCSP instead of CRLs where your PKI supports it. The OCSP responder option is a separate code path and not part of this advisory, although it has its own operational requirements, including how to handle a responder that is unreachable.
  3. Tighten who can obtain certificates and where trust is decided. Make sure the Certificate Authority subject DN option is set so Keycloak revalidates the chain against its truststore, restrict the truststore to the issuers you actually use, and avoid trusting CAs that issue certificates with arbitrary extensions.
  4. Limit what a mistaken path can reach. Run Keycloak as a non-root user in a container with a read-only root filesystem, mount only what it needs, and set a heap limit and a container memory limit so that a runaway parse restarts one pod and does not take down the host. Make sure a readiness probe and at least two replicas mean one failed pod does not end logins.
  5. Watch the logs. The distribution point value itself is logged only at TRACE level, which you should not enable in production. The path does show at error level through the “Unable to load CRL from” message when a load fails, so alert on that message and look for .. in the path it prints.

How do I patch when the fix arrives?

Once a release with the containment check is published, the upgrade is the same as any security point release: read the release notes, move to the patched version on your current minor line if one is offered (Keycloak has shipped point releases on several older 26.x lines, such as 26.4.x and 26.6.x, which is visible in the tag list), test an X.509 login against a staging realm, and roll it out one node at a time. Our 26.7.4 security fixes checklist walks through that process, including the test steps worth keeping after each update. After patching, test the configuration with a certificate that has a legitimate http CDP and, if you rely on relative CRL files, one that points at a file inside conf/, because a stricter containment check is the kind of change that surfaces a path you did not know you used.

Who handles the upgrade, a managed service or you?

If you host Keycloak yourself, you audit the flows, apply the interim changes and schedule the upgrade, as with the token exchange mTLS bypass, CVE-2026-97846 and the LDAP certificate validation CVE; on a managed Keycloak cluster such as those on our hosting page (see also our security page) upgrades are normally applied by the provider once a fixed release exists, but the decision to use the distribution point option stays in your realm configuration. For related reading on strong authentication, see NIST IR 8587 token protection and API authentication best practices.

FAQ

Does CVE-2026-103884 affect Keycloak if I do not use X.509 authentication?

No. The vulnerable code is in the X.509 client certificate authenticator, and it is reached only when a realm has that authenticator in a flow and CRL Distribution Point checking turned on. Realms that use only passwords, passkeys, social login or OIDC and SAML federation do not run this code.

Is CVE-2026-103884 a remote code execution or arbitrary file read vulnerability?

Neither, based on the advisory and the source I reviewed. In the code a file that is not a valid CRL causes the login to fail without returning its contents. The main risk is memory exhaustion from loading a very large file, plus limited probing of the file system.

Which Keycloak version fixes CVE-2026-103884?

I could not verify one. I could not find a fix on main or on the release tags I inspected, and there was no tag newer than 26.8.0 on 6 October 2026. The unguarded path code is present in 26.4.16, 26.6.7, 26.7.5 and 26.8.0, so check the Keycloak release notes before upgrading and do not assume your current minor line is clean.

Is the CRL path option I configure myself vulnerable?

Not through this advisory. The flaw concerns locations taken from the presented certificate when the distribution point option is on. A path you type into the CRL Path option is chosen by an administrator, not by the certificate holder, so it is not attacker-controlled, although you should still keep that file inside conf/.

Why is the property called jboss.server.config.dir in a Quarkus-based Keycloak?

It is a name inherited from earlier versions that the current code still reads. In Keycloak 26.x the launch scripts kc.sh and kc.bat set it to the conf directory, so the CRL loader resolves relative file paths there, and the authenticator’s help text still refers to it by that name.

Patched on the day, not on your next maintenance window

Keycloak 26.7.2 fixed CVE-2026-18963, an account takeover through password reset. Skycloak had it available for auto-upgrade and told customers the same day upstream shipped it. Self-hosted teams schedule that work themselves.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman