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-2025-14778
5.40.01%

Keycloak UMA: The 'First-Item-Wins' Access Control Disaster

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 10, 2026·6 min read·6 visits

PoC Available

Executive Summary (TL;DR)

Keycloak checks the ID of the driver but lets the whole carload of robbers in. A flaw in `UserManagedPermissionService` allowed users to update multi-resource policies by only proving ownership of the *first* resource. This lets User A modify access rules for User B's resources.

A logic flaw in Keycloak's User-Managed Access (UMA) Protection API allows for horizontal privilege escalation. By exploiting how the system validates resource ownership in shared policies, an attacker can modify permissions for resources they do not own, effectively hijacking access controls for other users.

The Hook: When Complexity Eats Security

User-Managed Access (UMA) 2.0 is a beast of a specification. It’s designed to give users granular control over their resources—think "Alice wants to share her vacation photos with Bob for 24 hours, but only if he promises not to download them." Implementing this in the real world requires a labyrinth of policies, permissions, and resource sets. Keycloak, the 800-pound gorilla of open-source Identity and Access Management (IAM), implements this via its Protection API.

But here's the thing about complex specifications: they often lull developers into a false sense of security regarding simple logic. CVE-2025-14778 is not a buffer overflow or a fancy deserialization gadget chain. It's a logic error so simple it hurts. It’s the digital equivalent of a bouncer checking the ID of the first person in a line of ten and waving everyone else through.

In this vulnerability, the component responsible for managing permissions—UserManagedPermissionService—forgot that a single policy might govern multiple resources. By validating ownership against only one of them, it opened the door for what we call Horizontal Privilege Escalation. If you share a policy with me, I can suddenly decide who gets to see your data.

The Flaw: The 'Party Bus' Logic Error

Let's strip away the IAM jargon and look at the logic. When a user sends a PUT request to update a UMA policy (permission), Keycloak needs to answer one question: "Does the person making this request actually own the resource being modified?"

In the vulnerable version of Keycloak, the code answered this question by grabbing the associated resource ID from the policy. The problem? A UMA policy can be associated with a list of resources. The code effectively said:

> "Grab the first resource ID from the list. Does the user own this one? Yes? Cool, apply the changes to the whole list."

This is the "Party Bus" fallacy. If the driver has a ticket, the bouncer assumes everyone on the bus has a ticket. If an attacker can craft or interact with a policy that includes their own resource (Resource A) and a victim's resource (Resource B), they become the driver of that bus. When they send an update command, Keycloak checks Resource A, sees the attacker owns it, gives the thumbs up, and inadvertently applies the new rules to Resource B as well.

The Code: Diff or Die

The fix provided in Pull Request #46154 exposes the smoking gun. The vulnerability existed in the update method of UserManagedPermissionService.java.

The Vulnerable Logic (Conceptual): In previous versions, the code implicitly trusted that checking one resource was enough. It didn't explicitly validate the integrity of the resource set being updated.

The Fix: The patch introduces strict validation to ensure that a user cannot change the resource association during an update if it involves more than the original resource they own. Look at this defensive check added in the patch:

// Get the ID of the resource associated with this permission
String resourceId = getAssociatedResourceId(policyId);
 
// Get the resources the user is trying to set in this update
Set<String> resources = Optional.ofNullable(representation.getResources()).orElse(Set.of());
 
// THE FIX: strict validation
if (resources.isEmpty() || (resources.size() == 1 && resources.contains(resourceId))) {
    // If we are only touching the original resource, proceed to check ownership
    checkRequest(resourceId, representation);
    // Proceed with update...
} else {
    // BLOCK: You cannot change the resource scope or add resources you don't own
    throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, 
        "Uma permission resource cannot be changed", Response.Status.BAD_REQUEST);
}

Analysis: The developers effectively locked down the API. You can no longer smuggle in new resources or modify a multi-resource set via this endpoint. If the resource list in your request isn't exactly just the one you own (or empty, implying no change), the server throws a BAD_REQUEST. This kills the attack vector dead by enforcing a 1:1 mapping during updates.

The Exploit: Hijacking the Policy

To exploit this, an attacker doesn't need to be an admin. They just need to be a regular user with the ability to create resources and interact with UMA policies. Here is the attack chain:

1. The Setup Imagine a collaborative environment (like a document sharing system) using Keycloak.

  • Attacker owns Document_Evil.
  • Victim owns Document_Sensitive.

2. The Link A UMA policy is created that groups these resources. This could happen legitimately in a shared workspace scenario, or the attacker might manipulate a "collection" mechanism to group Document_Evil and Document_Sensitive under one permission set.

3. The Attack The attacker sends a PUT request to /realms/{realm}/authz/protection/uma-policy/{policyId}.

{
  "name": "Attacker Controlled Policy",
  "description": "I am changing the rules now.",
  "logic": "POSITIVE",
  "decisionStrategy": "UNANIMOUS",
  "resources": ["Document_Evil", "Document_Sensitive"],
  "policies": ["Grant_Access_To_Attacker"]
}

4. The Execution Keycloak's checkRequest looks at the policy. It sees Document_Evil. It asks: "Does Attacker own Document_Evil?" The answer is YES.

The system approves the request. The policy is updated. Now, Grant_Access_To_Attacker applies to both documents. The attacker has successfully granted themselves full access to the victim's sensitive document, bypassing the fact that they do not own it.

The Impact: Why Should You Care?

While the CVSS score is a moderate 5.4 (due to the complexity of needing a shared policy context), the impact in the right environment is critical. This is Broken Access Control in its purest form.

  • Data Leakage: An attacker can grant read permissions to themselves for files they shouldn't see.
  • Data Integrity: An attacker could modify the policy to deny access to the legitimate owner, causing a Denial of Service (DoS) for that resource.
  • Stealth: Because the check passes a valid ownership test (for the attacker's resource), logs might not look immediately suspicious. It looks like a user updating their own policy.

In highly regulated industries (Finance, Healthcare) where UMA is often used for consent management, this could allow a malicious actor to rewrite patient consent forms or financial access rules simply by bundling them with a record they control.

The Fix: Closing the Loophole

The mitigation is straightforward: Update Keycloak. The patch doesn't just fix the bug; it changes the behavior of the API to be strict about resource boundaries during updates.

Patching Strategy:

  • Keycloak 26.2 Users: Upgrade to 26.2.13-1 or 26.2-15 immediately.
  • Keycloak 26.4 Users: Upgrade to 26.4.9-1.

There are no viable workarounds. Red Hat has explicitly stated that trying to mitigate this via configuration is insufficient. You need the code change that enforces the logic check. If you are running a custom build of Keycloak, you need to cherry-pick PR #46154 into your branch and rebuild the services module.

Lesson Learned: When validating permissions for a collection, validate the whole collection. Iterating through a list is computationally cheap; a data breach is expensive.

Official Patches

Red HatRed Hat Security Advisory RHSA-2026:2363
Red HatRed Hat Bugzilla #2422600

Fix Analysis (1)

Technical Appendix

CVSS Score
5.4/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L
EPSS Probability
0.01%
Top 99% most exploited

Affected Systems

Red Hat build of Keycloak 26.2Red Hat build of Keycloak 26.4Keycloak (upstream) prior to patched releases

Affected Versions Detail

Product
Affected Versions
Fixed Version
Red Hat build of Keycloak
Red Hat
< 26.2.13-126.2.13-1
Red Hat build of Keycloak
Red Hat
< 26.4.9-126.4.9-1
AttributeDetail
CWE IDCWE-266
Attack VectorNetwork (Remote)
CVSS Score5.4 (Medium)
ImpactHorizontal Privilege Escalation
Exploit StatusPoC Available (Internal/Theoretical)
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1098Account Manipulation
Persistence
T1078Valid Accounts
Defense Evasion
T1212Exploitation for Credential Access
Credential Access
CWE-266
Incorrect Privilege Assignment

Incorrect Privilege Assignment

Known Exploits & Detection

Internal AnalysisExploit logic inferred from patch diff and bug report description.

Vulnerability Timeline

Vulnerability reported to Red Hat Bugzilla
2025-12-16
CVE-2025-14778 publicly disclosed
2026-02-09
Patches released (26.2.13-1, 26.4.9-1)
2026-02-09

References & Sources

  • [1]Red Hat CVE Database: CVE-2025-14778
  • [2]GitHub PR #46154: Fix for UMA permission update

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.