Privilege Escalation / Authorization bypass

MEDIUM
grafana/grafana
Commit: e095f32e7a19
Affected: <=12.4.0
2026-06-10 09:56 UTC

Description

The commit changes how resource permission subjects are looked up for RBAC resource permissions. Previously, the code used a service identity to resolve subject details (users/teams), which could enable impersonation or leakage of subject identities during permission resolution. The fix switches to using the caller's user context to fetch user and team details, aligning permission resolution with the actual caller identity and reducing the risk of authorization bypass when querying RBAC data.

Proof of Concept

Prerequisites: - Grafana instance with RBAC enabled and a resource that has permissions assigned to a specific user or team. - An attacker accounts with limited privileges who can read resource permissions but should not be able to read other user details. Exploit (pre-fix behavior): 1) Authenticate as attacker (user A) who can view resource permissions but is not allowed to read other users. 2) Call the API that returns resource permissions, e.g. GET /api/resource-permissions?resource_id=RESOURCE_ID. 3) Observe the response includes subject details for other users/teams (e.g., userID, userUID) resolved via service identity rather than the caller context. Example response snippet: { "permissions": [ { "kind": "User", "name": "bob", "userID": "bob-id", "userUID": "bob-uid", "role": "Editor" } ] } Impact: - Attacker can learn exact identities (IDs/UIDs) of other users who have permissions on the resource, which could enable targeted attacks (phishing, social engineering, or further RBAC abuse) or exploitation of any downstream workflows that rely on user identity. - This constitutes an authorization-bypass risk if the subject details are supposed to be restricted to permitted viewers only. Fix (post-fix behavior): - The code now uses the caller's user context when resolving subject details (GetByUID/GetTeamByID with ctx, instead of a service-identity derived context). - This ensures that only information the caller is allowed to see is exposed, preventing leakage of other users/teams identities via permission lookups. PoC (after fix you should no longer see sensitive subject details): 1) Authenticate as attacker A with limited view permissions for the resource. 2) Request resource permissions as before. 3) Observe that subject details (userID/userUID) for other users are redacted or omitted, depending on caller permissions. Note: Exact API endpoints/response shapes may vary; the key verification is that subject details are exposed in the pre-fix path and are suppressed after the fix.

Commit Details

Author: Gabriel MABILLE

Date: 2026-06-10 09:34 UTC

Message:

`RBAC`: Use user context to fetch resource permissions (#126134)

Triage Assessment

Vulnerability Type: Privilege Escalation / Authorization bypass

Confidence: MEDIUM

Reasoning:

The commit changes how resource permission subjects (users/teams) are looked up: it removes resolving subject details via a service identity and instead uses the caller's user context. This reduces the risk of impersonation or bypass of per-user permissions when querying RBAC data, aligning permission resolution with the actual caller's identity.

Verification Assessment

Vulnerability Type: Privilege Escalation / Authorization bypass

Confidence: MEDIUM

Affected Versions: <=12.4.0

Code Diff

diff --git a/pkg/services/accesscontrol/resourcepermissions/api_adapter.go b/pkg/services/accesscontrol/resourcepermissions/api_adapter.go index 1eae5c9167ac..d9c0e441e685 100644 --- a/pkg/services/accesscontrol/resourcepermissions/api_adapter.go +++ b/pkg/services/accesscontrol/resourcepermissions/api_adapter.go @@ -24,7 +24,6 @@ import ( folderv1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1" iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1" "github.com/grafana/grafana/pkg/api/dtos" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/accesscontrol" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" @@ -122,11 +121,6 @@ func (a *api) convertK8sResourcePermissionToDTO(ctx context.Context, resourcePer } orgID := namespaceInfo.OrgID - // Resolve subject names with a service identity: the caller is already authorized - // to read this resource's permissions but may lack users:read (e.g. an editor), - // which would otherwise leave the subject unnamed. Mirrors the legacy SQL join. - lookupCtx, _ := identity.WithServiceIdentity(ctx, orgID) - permissions := resourcePerm.Spec.Permissions dto := make(getResourcePermissionsResponse, 0, len(permissions)) @@ -160,7 +154,7 @@ func (a *api) convertK8sResourcePermissionToDTO(ctx context.Context, resourcePer switch kind { case iamv0.ResourcePermissionSpecPermissionKindUser, iamv0.ResourcePermissionSpecPermissionKindServiceAccount: - userDetails, err := a.service.userService.GetByUID(lookupCtx, &user.GetUserByUIDQuery{UID: name}) + userDetails, err := a.service.userService.GetByUID(ctx, &user.GetUserByUIDQuery{UID: name}) if err == nil { permDTO.UserID = userDetails.ID permDTO.UserUID = userDetails.UID @@ -171,7 +165,7 @@ func (a *api) convertK8sResourcePermissionToDTO(ctx context.Context, resourcePer permDTO.ID = a.getRoleIDFromK8sObject(permDTO.RoleName, orgID) } case iamv0.ResourcePermissionSpecPermissionKindTeam: - teamDetails, err := a.service.teamService.GetTeamByID(lookupCtx, &team.GetTeamByIDQuery{ + teamDetails, err := a.service.teamService.GetTeamByID(ctx, &team.GetTeamByIDQuery{ UID: name, OrgID: orgID, })
← Back to Alerts View on GitHub →