PoC Archive PoC Archive
Critical CVE-2025-41115 unpatched

Grafana Enterprise SCIM User ID Collision / Impersonation (CVE-2025-41115)

by I3r1h0n · 2026-07-06

CVSS 10.0/10
Severity
Critical
CVE
CVE-2025-41115
Category
web
Affected product
Grafana Enterprise / Grafana Cloud, SCIM provisioning component (/api/scim/v2/Users)
Affected versions
Grafana 12.x+ with SCIM provisioning configured (enableSCIM feature flag + user_sync_enabled set); fixed by upstream commit ca5d89812015ef2db3acc62826f73650450b331e
Disclosed
2026-07-06
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07-06
Author / ResearcherI3r1h0n
CVE / AdvisoryCVE-2025-41115
Categoryweb
SeverityCritical
CVSS Score10.0 (per NVD)
StatusPoC
Tagsgrafana, grafana-enterprise, scim, user-impersonation, privilege-escalation, id-collision, python, docker, cwe-287
RelatedN/A

Affected Target

FieldValue
Software / SystemGrafana Enterprise / Grafana Cloud, SCIM provisioning component (/api/scim/v2/Users)
Versions AffectedGrafana 12.x+ with SCIM provisioning configured (enableSCIM feature flag + user_sync_enabled set); fixed by upstream commit ca5d89812015ef2db3acc62826f73650450b331e
Language / PlatformPython (exploit script, using uv/toml/requests); target is Go-based Grafana Enterprise, test stand via Docker Compose
Authentication RequiredYes — requires a valid Grafana service-account Bearer token with SCIM API access
Network Access RequiredYes (HTTP/HTTPS to the Grafana Enterprise SCIM API)

Summary

Grafana Enterprise/Cloud’s SCIM provisioning feature (enabled via the enableSCIM feature flag together with user_sync_enabled) fails to properly validate the externalId supplied when a SCIM client creates a user via POST /api/scim/v2/Users. This lets a caller with SCIM API access overwrite or collide with the internal ID of an existing Grafana user, effectively allowing the newly-provisioned SCIM identity to take over/impersonate that internal user — leading to privilege escalation (e.g. impersonating an existing Admin account). The repository ships a Docker Compose “stand” that spins up a real Grafana Enterprise instance with SCIM enabled (requiring a valid Enterprise license) plus a small Python exploit that posts a crafted SCIM user-creation payload with a chosen externalId targeting the victim’s internal user ID. The upstream fix adds explicit checks preventing this ID collision during SCIM user provisioning.


Vulnerability Details

Root Cause

When SCIM provisioning is enabled (auth.scim config with user_sync_enabled = true), Grafana’s SCIM user-creation endpoint accepted an attacker/client-supplied externalId without verifying it doesn’t collide with the internal ID of a pre-existing (non-SCIM) user. Because Grafana links SCIM-provisioned identities to internal user records via this ID, a crafted externalId matching a victim’s internal ID lets the SCIM-created identity assume/override that user’s identity:

1
2
3
4
5
6
7
8
9
payload = {
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": account,
    "externalId": uid,          # attacker-chosen; collides with victim's internal user id
    "name": {"formatted": "Pwner"},
    "emails": [{"value": account, "primary": True}],
    "active": True
}
r = requests.post(f"{target}/api/scim/v2/Users", json=payload, headers=headers, verify=False)

The upstream fix (commit ca5d89812015ef2db3acc62826f73650450b331e) adds checks to reject SCIM user-provisioning requests whose externalId would collide with an existing internal user ID.

Attack Vector

  1. Target Grafana Enterprise/Cloud instance has SCIM provisioning enabled (enableSCIM feature flag) and user_sync_enabled = true, with reject_non_provisioned_users typically false.
  2. Attacker obtains (or is issued) a service-account Bearer token with access to the SCIM API (e.g. via POST /api/serviceaccounts + /tokens, as shown in the stand’s init.sh).
  3. Attacker identifies the internal user ID (uid) of a victim account they wish to impersonate (e.g. an existing Admin).
  4. Attacker sends POST /api/scim/v2/Users with a SCIM user payload whose externalId is set to the victim’s internal uid, and userName/emails set to an attacker-controlled account name.
  5. Due to the missing collision check, Grafana’s SCIM sync links the newly created SCIM identity to the victim’s internal user record, allowing the attacker’s SCIM-managed account to impersonate or assume the privileges of the victim’s account.

Impact

An attacker with SCIM API access (service-account token) can impersonate an existing Grafana user — including administrators — resulting in privilege escalation and unauthorized access to dashboards, data sources, and administrative functions within the affected Grafana Enterprise/Cloud organization.


Environment / Lab Setup

Target (stand):
  - Docker Compose stack running grafana/grafana-enterprise:12.0
  - Valid Grafana Enterprise license key placed at ./stand/license.jwt
  - grafana.ini: [auth.scim] user_sync_enabled = true, group_sync_enabled = true,
    reject_non_provisioned_users = false; [enterprise] license_path = /secrets/license.jwt
  - stand/init.sh creates an Admin-role service account and prints its API token

Attacker:
  - Python 3 with `uv` (or pip) + `requests`, `toml`
  - exploit/resource/config.toml populated with the service-account token,
    victim account/uid, and target URL

Proof of Concept

PoC Script

See exploit/src/main.py, exploit/resource/config.toml, and the stand/ Docker Compose test bed in this folder.

1
2
3
4
5
6
7
cp /path/to/your/license.jwt stand/license.jwt
cd stand
docker compose up -d

./init.sh
cd ../exploit
uv run src/main.py

Detection & Indicators of Compromise

Signs of compromise:

  • New SCIM-provisioned user records whose externalId matches an existing user’s internal ID
  • Unexpected changes in ownership/permissions on an existing (especially Admin) account
  • Service-account tokens with SCIM API scope used outside of the expected IdP-sync process
  • Audit log entries showing SCIM user creation/updates that do not correspond to the configured identity provider’s sync jobs

Remediation

ActionDetail
Primary fixUpgrade Grafana Enterprise to a version including commit ca5d89812015ef2db3acc62826f73650450b331e, which adds validation preventing SCIM externalId collisions with existing internal user IDs
Interim mitigationRestrict issuance of SCIM-scoped service-account tokens to trusted automation only; set reject_non_provisioned_users = true where feasible; monitor SCIM user-provisioning requests for externalId values matching existing user IDs

References


Notes

Mirrored from https://github.com/I3r1h0n/GrafanaSCIMalform on 2026-07-06. Contains an exploit/ directory (Python project managed via uv) plus a stand/ Docker Compose test bed reproducing the Grafana SCIM flaw with a real Grafana Enterprise image; the stand requires a valid Grafana Enterprise license (license.jwt) to actually enable SCIM. Note: as shipped, exploit/src/main.py’s main() calls sys.exit(1) immediately after logging “Exploit started” and before it loads config.toml or calls exploit() — the exploit logic itself is intact and functional if that early exit is removed, but the script does not run the actual POST request out of the box.