CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-9083

CVE-2026-9083: Arbitrary Filesystem Path Probing in Keycloak Java Keystore Key Provider

Alon Barad
Alon Barad
Software Engineer

Oct 30, 2026·11 min read·4 visits

Executive Summary (TL;DR)

Authenticated Keycloak realm administrators with the 'manage-realm' role can exploit unvalidated file path handling in the java-keystore key provider to enumerate arbitrary files and directories on the underlying server filesystem via differential error responses.

CVE-2026-9083 is a path traversal and information disclosure vulnerability (CWE-22) affecting Keycloak and the Red Hat build of Keycloak. The vulnerability resides in the Java Keystore key provider component (JavaKeystoreKeyProviderFactory), which fails to restrict the user-supplied keystore file path to a designated directory. An authenticated realm administrator holding the 'manage-realm' role can supply arbitrary absolute or relative filesystem paths when configuring a key provider. Because the application returns distinct error messages depending on whether the target path does not exist, lacks read permissions, or fails keystore parsing, an attacker can use this component as an oracle to probe the underlying host filesystem.

Vulnerability Overview

Keycloak is an open-source identity and access management solution that supports multi-tenant deployments through isolated security domains known as realms. Each realm manages its own set of users, credentials, roles, and cryptographic keys used for signing and encrypting tokens. To accommodate external key management workflows, Keycloak provides a Key Service Provider Interface (SPI) that allows realm administrators to configure custom key providers, including the java-keystore provider (org.keycloak.keys.JavaKeystoreKeyProviderFactory).

The java-keystore provider enables a realm to load asymmetric key pairs or symmetric secret keys directly from a Java KeyStore (JKS, PKCS12, or BCFKS) file residing on the local server filesystem. When configuring this component through the Keycloak Admin Console or the Admin REST API, a realm administrator supplies configuration parameters including the keystore file path (keystore), password (keystorePassword), key alias (keyAlias), and keystore type (keystoreType).

In affected versions of Keycloak, the JavaKeystoreKeyProviderFactory and JavaKeystoreKeyProvider classes do not enforce path containment or directory restrictions on the keystore configuration attribute. The component accepts arbitrary absolute paths or relative paths containing directory traversal sequences (../) and passes them directly to a java.io.FileInputStream constructor.

This design flaw introduces an information disclosure vulnerability classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). An authenticated realm administrator with the manage-realm role can exploit the differential error responses generated during keystore validation to probe the host filesystem. By observing the specific exceptions returned by the server, a privileged user within a single tenant realm can determine the existence and readability of arbitrary files across the underlying operating system or within other realms.

Root Cause Analysis

The root cause of CVE-2026-9083 is the absence of path canonicalization and directory boundary validation during the configuration validation and key loading phases of the java-keystore provider lifecycle. When a realm administrator creates or updates a java-keystore component, Keycloak invokes the validateConfiguration method in org.keycloak.keys.JavaKeystoreKeyProviderFactory.

Prior to the patch, validateConfiguration verified only the presence and basic syntactic validity of the required configuration properties (keystore, keystorePassword, keyAlias, keyPassword, and keystoreType) using ConfigurationValidationHelper. It did not verify whether the string supplied in the keystore property resolved to a location within an authorized directory tree. Immediately following property presence checks, validateConfiguration instantiated JavaKeystoreKeyProvider and invoked its loadKey method.

Within org.keycloak.keys.JavaKeystoreKeyProvider.loadKey, the raw keystore string was retrieved from the ComponentModel and passed directly into new FileInputStream(keystorePath). Because Java's FileInputStream resolves paths directly against the host operating system's filesystem, any path accessible to the OS user running the Keycloak JVM process could be targeted.

The vulnerability is exposed as an information disclosure oracle due to the exception handling blocks in validateConfiguration. When loadKey is executed against an arbitrary file path, three distinct execution branches can occur depending on the state of the target path on the host filesystem:

  1. If the target file does not exist, or if it exists but the Keycloak OS process lacks read permissions, new FileInputStream(keystorePath) throws a java.io.FileNotFoundException. Note that in standard Java FileInputStream implementations, both non-existent files and permission-denied errors raise FileNotFoundException, which validateConfiguration catches and translates into a ComponentValidationException with the message "Failed to load keys. File not found on server."
  2. If the target path points to an existing directory rather than a regular file, FileInputStream also raises a FileNotFoundException.
  3. If the target file exists and is readable by the Keycloak process, FileInputStream succeeds and passes the stream to KeyStore.load(). If the target file is not a valid keystore (e.g., /etc/passwd or an application configuration file), KeyStore.load() throws an IOException (caught and reported as "Failed to load keys. Error while reading keys file." or a GeneralSecurityException). If the target file is a valid keystore belonging to another realm, it may fail with a password verification error or alias error. This clear discrepancy allows reliable differentiation between readable existing files and non-existent or unreadable paths.

Code Analysis

The remediation for CVE-2026-9083, implemented in commit 1bc0dd3ea7129f53d3031687e7bf36c009cf1bca, introduces a strict directory hierarchy for keystore files and enforces path containment checks before any file I/O operations take place. The patch modifies both JavaKeystoreKeyProviderFactory.java and JavaKeystoreKeyProvider.java.

In JavaKeystoreKeyProviderFactory.java, the patch introduces a new initialization configuration parameter keystores-path (KEYSTORES_PATH_INIT_KEY). During the factory initialization phase (init), Keycloak sets the base keystoresPath directory, defaulting to ${kc.home.dir}/data if not explicitly configured via startup flags:

@Override
public void init(Config.Scope config) {
    // Define and normalize the root directory for all realm keystores
    this.keystoresPath = Paths.get(config.get(KEYSTORES_PATH_INIT_KEY, System.getProperty("kc.home.dir") + "/data")).normalize();
    String[] supportedKeystoreTypes = CryptoIntegration.getProvider().getSupportedKeyStoreTypes()
            .map(KeystoreUtil.KeystoreFormat::toString)
            .toArray(String[]::new);
    // ...
}

Within validateConfiguration, the patch adds path normalization and boundary verification prior to invoking loadKey. The user-supplied keystore path is converted to a java.nio.file.Path object and normalized to strip redundant relative segments (such as . and ..). If the path is relative, it is resolved against a realm-specific subdirectory (${keystoresPath}/${realm.getName()}). The code then verifies that the normalized path begins with the realm's designated directory using Path.startsWith():

// Convert input to Path and resolve any '../' traversal sequences
Path keystorePath = Paths.get(model.get(KEYSTORE_KEY)).normalize();
if (!keystorePath.isAbsolute()) {
    keystorePath = this.keystoresPath.resolve(realm.getName()).resolve(keystorePath);
}
// Enforce containment within ${keystoresPath}/${realmName}
if (!keystorePath.startsWith(keystoresPath.resolve(realm.getName()))) {
    throw new ComponentValidationException(String.format(
            "Keystore file '%s' is not under the realm directory '%s'", keystorePath, keystoresPath.resolve(realm.getName())));
}
 
try {
    KeyWrapper key = new JavaKeystoreKeyProvider(keystoresPath, realm, model, session.vault()).loadKey(keystoresPath, realm, model);
    validateCertificateChain(key.getCertificateChain());
} // ...

A subtle flaw exists in the ordering of operations for relative paths containing traversal sequences: because .normalize() is called before this.keystoresPath.resolve(realm.getName()).resolve(keystorePath) and not called again after .resolve(), a relative input such as ../other_realm/keystore.jks remains ../other_realm/keystore.jks after the initial .normalize(). When resolved against /opt/keycloak/data/myrealm, the resulting Path object is /opt/keycloak/data/myrealm/../other_realm/keystore.jks. Because Path.startsWith() operates on path elements without automatically normalizing them, /opt/keycloak/data/myrealm/../other_realm/keystore.jks actually passes the startsWith("/opt/keycloak/data/myrealm") check.

> [!WARNING] > Fix Completeness Assessment: While the patch blocks arbitrary absolute paths (such as /etc/passwd) and relative paths that are normalized after resolution, calling .normalize() prior to .resolve() without re-normalizing the resolved path before calling .startsWith() is a known anti-pattern in Java Path validation. If a relative path beginning with .. is supplied, keystorePath.isAbsolute() is false, and .resolve(keystorePath) appends the .. segment onto the base realm path. Because Path.startsWith() compares abstract path components literally without resolving .., the prefix check evaluates to true, potentially allowing relative path traversal outside the realm directory if not normalized post-resolution.

In JavaKeystoreKeyProvider.java, loadKey was updated to resolve relative paths against the realm directory while maintaining backward compatibility for existing, unmodified component configurations created prior to upgrading:

protected KeyWrapper loadKey(Path keystoresPath, RealmModel realm, ComponentModel model) {
    Path keystorePath = Paths.get(model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_KEY));
    if (!keystorePath.isAbsolute()) {
        // Resolve the path from the keystores directory if file exists;
        // if not, fall back to previous file location for backwards compatibility
        Path path = keystoresPath.resolve(realm.getName()).resolve(keystorePath);
        if (Files.exists(path)) {
            keystorePath = path;
        }
    }
    try (FileInputStream is = new FileInputStream(keystorePath.toFile())) {
        KeyStore keyStore = loadKeyStore(is, keystorePath.toString());
        // ...

This backward-compatibility mechanism ensures that pre-existing keystores configured in earlier versions continue to load at runtime until an administrator attempts to modify or re-import the component configuration, at which point validateConfiguration is triggered.

Exploitation

Exploiting CVE-2026-9083 requires authenticated access to a Keycloak realm with an account possessing the manage-realm realm-management role. In multi-tenant deployments where realm administration is delegated to customer administrators or lower-trust organizational units, an attacker with these credentials can interact directly with the Keycloak Admin REST API to probe the underlying server filesystem.

To execute the probe, the attacker sends an HTTP POST request to the /admin/realms/{realm}/components endpoint (or PUT to /admin/realms/{realm}/components/{id}) with a JSON payload defining a org.keycloak.keys.KeyProvider component using the java-keystore provider ID.

POST /admin/realms/target-realm/components HTTP/1.1
Host: keycloak.example.com
Authorization: Bearer <Admin_Access_Token>
Content-Type: application/json
 
{
  "name": "probe-keystore",
  "providerId": "java-keystore",
  "providerType": "org.keycloak.keys.KeyProvider",
  "parentId": "target-realm",
  "config": {
    "keystore": ["/etc/passwd"],
    "keystorePassword": ["password"],
    "keyAlias": ["alias"],
    "keyPassword": ["password"],
    "keystoreType": ["JKS"],
    "active": ["true"],
    "enabled": ["true"],
    "priority": ["100"],
    "algorithm": ["RS256"]
  }
}

By iterating through a list of target paths in the keystore array field, the attacker observes the HTTP 400 Bad Request response body returned by the server:

  • Scenario A (Non-existent file, directory, or unreadable file): When probing /nonexistent/file or /etc/shadow (assuming Keycloak runs as a non-root user), the server responds with:
    {
      "errorMessage": "Failed to load keys. File not found on server."
    }
  • Scenario B (Existing, readable non-keystore file): When probing /etc/passwd or /opt/keycloak/conf/keycloak.conf, FileInputStream opens the file and KeyStore.load() fails to parse the magic bytes, returning:
    {
      "errorMessage": "Failed to load keys. Error while reading keys file."
    }
  • Scenario C (Existing, readable keystore file with wrong password): When probing a valid JKS or PKCS12 file on the system, KeyStore.load() throws a GeneralSecurityException (such as Keystore was tampered with, or password was incorrect), returning:
    {
      "errorMessage": "Failed to load keys. Keystore was tampered with, or password was incorrect"
    }

Furthermore, in Scenarios C or when probing another realm's keystore file whose password happens to be known or weak, a realm administrator in Realm A can load cryptographic keys belonging to Realm B if the filesystem path to Realm B's keystore is discovered.

Impact Assessment

The primary impact of CVE-2026-9083 is unauthorized information disclosure (C:H in the CVSS v3.1 vector CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N). Although the vulnerability does not allow reading the arbitrary contents of non-keystore files directly through the HTTP response, it provides a reliable oracle to enumerate the filesystem layout, verify installed packages, locate configuration files, and identify readable keystore files on the server.

In a multi-tenant Keycloak environment where realm administration (manage-realm) is granted to semi-trusted parties, this vulnerability breaks tenant isolation at the filesystem level. A realm administrator is intended to have administrative control solely over their assigned realm's logical configuration, not visibility into the underlying host operating system or the file structures of neighboring realms.

If a realm administrator locates a valid Java KeyStore file on the host filesystem belonging to another realm or to the host environment itself, and either knows the keystore password or brute-forces it via repeated component validation requests, the administrator can load those cryptographic keys into their own realm. Once loaded, those keys could be used by the attacker's realm to sign tokens or inspect public certificate chains.

The vulnerability does not allow arbitrary file modification (I:N) or deletion, nor does it degrade the availability of the Keycloak server (A:N). Because exploitation requires high privileges (PR:H), specifically an authenticated user with the manage-realm role, the Exploit Prediction Scoring System (EPSS) score is low at 0.0078 (55.84th percentile).

Remediation

To remediate CVE-2026-9083, upgrade Keycloak to version 26.6.4 or 26.4.13 (or later). Users of the Red Hat build of Keycloak should apply the corresponding security errata (RHSA-2026:30049, RHSA-2026:30050, RHSA-2026:30083, or RHSA-2026:30084), which update the packages to versions 26.4.13-1 and 26.6.4-2.

> [!IMPORTANT] > Breaking Change & Migration Requirement: The security patch introduces a breaking change for deployments utilizing the java-keystore key provider. Keystore files must now reside inside a realm-specific subdirectory under the main keystores directory: ${kc.home.dir}/data/<realm-name>/. While existing keystore configurations will continue to function at runtime after upgrading, any attempt to update, save, or import a java-keystore component pointing outside this directory will fail validation.

When migrating an existing deployment to a patched version, administrators must perform the following steps:

  1. Create a subdirectory matching the exact realm name inside ${kc.home.dir}/data/ (for example, /opt/keycloak/data/myrealm/).
  2. Alternatively, configure a custom base directory for keystores at server startup using the --spi-keys--java-keystore--keystores-path CLI flag:
    kc.sh start --spi-keys--java-keystore--keystores-path /path/to/keystores
  3. Move or copy each realm's keystore file into its corresponding <keystores-path>/<realm-name>/ directory and ensure the Keycloak OS user has read permissions.
  4. Update the keystore configuration property in the Keycloak Admin Console for each java-keystore provider to use either the relative filename (e.g., keystore.jks) or the full canonical path inside the realm directory.

In environments where an immediate upgrade is not possible, mitigate the risk by auditing and restricting the assignment of the manage-realm role. Ensure that only fully trusted system administrators possess permissions to create or modify realm components.

Official Patches

KeycloakUpstream GitHub commit restricting Java keystore files to a realm-specific directory
Red HatRed Hat Security Advisory RHSA-2026:30049
Red HatRed Hat Security Advisory RHSA-2026:30050
Red HatRed Hat Security Advisory RHSA-2026:30083
Red HatRed Hat Security Advisory RHSA-2026:30084

Fix Analysis (1)

Technical Appendix

CVSS Score
4.9/ 10
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N
EPSS Probability
0.78%
Top 44% most exploited

Affected Systems

KeycloakRed Hat build of Keycloak

Affected Versions Detail

Product
Affected Versions
Fixed Version
Keycloak
Keycloak
>= 26.6.0, < 26.6.426.6.4
Keycloak
Keycloak
>= 26.4.0, < 26.4.1326.4.13
Red Hat build of Keycloak
Red Hat
>= 26.6, < 26.6.4-226.6.4-2
Red Hat build of Keycloak
Red Hat
>= 26.4, < 26.4.13-126.4.13-1
AttributeDetail
Vulnerability TypeCWE-22: Path Traversal / Arbitrary Path Probing
Affected ComponentJavaKeystoreKeyProviderFactory (java-keystore SPI)
Attack VectorNetwork (Keycloak Admin REST API / UI)
Privileges RequiredHigh ('manage-realm' role)
CVSS v3.1 Score4.9 Medium (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N)
EPSS Score0.78% (55.84th percentile)
Security ImpactHost filesystem enumeration and cross-realm keystore probing
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1083File and Directory Discovery
Discovery
T1005Data from Local System
Collection
CWE-22
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.

Vulnerability Timeline

Vulnerability logged in Red Hat Bugzilla (RHBZ#2480168).
2026-05-20
Official patch committed to the keycloak/keycloak repository (commit 1bc0dd3ea7129f53d3031687e7bf36c009cf1bca).
2026-06-09
CVE-2026-9083 published by Red Hat CNA and NVD alongside Red Hat Security Advisories RHSA-2026:30049, RHSA-2026:30050, RHSA-2026:30083, and RHSA-2026:30084.
2026-06-25
CVE record updated.
2026-06-26
NVD database record updated with CVSS and CPE metadata.
2026-07-01

References & Sources

  • [1]Red Hat CVE-2026-9083 Security Page
  • [2]Red Hat Bugzilla RHBZ#2480168
  • [3]Keycloak Fix Commit 1bc0dd3ea7129f53d3031687e7bf36c009cf1bca
  • [4]NVD Detail for CVE-2026-9083
  • [5]CVE.org Record for CVE-2026-9083
  • [6]Red Hat Security Advisory RHSA-2026:30049
  • [7]Red Hat Security Advisory RHSA-2026:30050
  • [8]Red Hat Security Advisory RHSA-2026:30083
  • [9]Red Hat Security Advisory RHSA-2026:30084

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 1 hour ago•CVE-2026-9799
4.6

CVE-2026-9799: Authorization Bypass via UMA Permission Ticket Wildcard in Keycloak

CVE-2026-9799 is an authorization bypass vulnerability in the User-Managed Access (UMA) subsystem of Keycloak (org.keycloak.authorization). When a resource server is configured with the non-default PERMISSIVE policy enforcement mode, an authenticated user holding a valid UMA permission ticket for a single typed resource can obtain an authorization Requesting Party Token (RPT) granting access to all resources of the same type across the resource server. By submitting an authorization request with the type selector prefix 'resource-type-any:<type>', the attacker bypasses instance-level owner approval checks for unshared peer resources that lack explicit static administrative policies.

Alon Barad
Alon Barad
1 views•9 min read
•about 3 hours ago•CVE-2026-9086
7.3

CVE-2026-9086: Stored Cross-Site Scripting via Case-Sensitive URI Scheme Validation Bypass in Keycloak

CVE-2026-9086 is a stored Cross-Site Scripting (XSS) vulnerability in Keycloak and the Red Hat build of Keycloak. The vulnerability stems from a case-sensitive URI scheme validation check in DefaultClientValidationProvider, allowing attackers with client registration or management permissions to inject javascript: or data: URIs using mixed-case characters.

Amit Schendel
Amit Schendel
5 views•10 min read
•about 4 hours ago•CVE-2026-62290
7.3

CVE-2026-62290: ClusterIssuer DNS01 Solver Policy Bypass and Credential Exfiltration in cert-manager

CVE-2026-62290 is an incorrect authorization vulnerability (CWE-863) in cert-manager that allows low-privileged Kubernetes namespace tenants to bypass ClusterIssuer DNS01 solver policies and exfiltrate cluster-scoped DNS credentials. By directly creating standalone ACME Challenge custom resources rather than going through the standard Certificate and Order workflow, an attacker can specify arbitrary solver provider configurations while referencing a privileged ClusterIssuer. The cert-manager ACME challenge controller reconciles these directly instantiated Challenge resources without verifying their parentage or validating the user-supplied solver parameters against the referenced ClusterIssuer's selector policies.

Amit Schendel
Amit Schendel
7 views•9 min read
•about 5 hours ago•CVE-2026-118662
6.9

CVE-2026-118662: Access Control Bypass via Uncanonicalized Query Parameters in Vite Development Server

CVE-2026-118662 (also tracked as GHSA-8g5g-mxqh-grgj) is an improper access control and path canonicalization vulnerability in the Vite frontend build and development tool. In version 6.4.4, the Vite development server fails to strip transform and loader query parameters before evaluating file paths against the `server.fs.deny` configuration directive. When the development server is exposed to a network and sensitive files located within `server.fs.allow` match patterns in `server.fs.deny`, an unauthenticated remote attacker can bypass file access restrictions by appending specific query strings such as `?raw` or `?inline` to HTTP requests. This allows arbitrary retrieval of restricted files, including environment configuration files and private cryptographic keys.

Alon Barad
Alon Barad
8 views•11 min read
•about 6 hours ago•CVE-2026-115909
7.5

CVE-2026-115909: Unbounded Symlink-Target Allocation Causes Process Abort in Rust zip Crate

A vulnerability in the Rust `zip` crate (versions 1.3.0 prior to 9.0.0) allows an attacker to trigger an uncatchable process abort via a crafted ZIP archive. When extracting symbolic link entries on Unix or Windows targets, `ZipArchive::extract` passes the untrusted, attacker-controlled `uncompressed_size` header value directly to `Vec::with_capacity` before reading or validating entry payload data. By supplying a 112-byte archive with a ZIP64 extended information field declaring an excessive uncompressed size, an attacker forces the memory allocator to fail and invoke `handle_alloc_error()`, immediately terminating the entire process with `SIGABRT`.

Alon Barad
Alon Barad
5 views•11 min read
•about 7 hours ago•CVE-2026-115906
6.5

CVE-2026-115906: AES Entry HMAC Authentication Bypass in Rust zip Crate

CVE-2026-115906 is a cryptographic authentication bypass vulnerability in the Rust zip library (zip-rs/zip2) affecting versions from 0.6.0 prior to 9.0.0. In the WinZip AE-x AES decryption reader implementation within src/aes.rs, the HMAC-SHA1-80 authentication tag is only validated when the internal byte counter (data_remaining) decrements to exactly zero. Because this counter is initialized from the unauthenticated compressed_size header field, an attacker can craft a ZIP archive where the entry stream hits premature physical end-of-file (EOF) before data_remaining reaches zero, causing the reader to return Ok(0) and skip HMAC verification entirely.

Alon Barad
Alon Barad
7 views•13 min read