CVE-2026-41657

mediumpriority · track · 0OT risk · elevatedEPSS 0.003 · 25 %ileEUUS
grimoire priorityP3 · Track
Exploitable?
—
no known exploitation
Likely / severe?
0.32% (25th) EPSS
CVSS 4.9 · medium
Is it mine?
2 products
Check your exposure
Who says so?
3 sources
cnnvd · enisa_euvd · osv

EPSS · scored 2026-10-27

Admidio is an open-source user management solution. Prior to version 5.0.9, the contacts_data.php endpoint uses a weaker permission check (isAdministratorUsers(), requiring only rol_edit_user=true) than the frontend UI (contacts.php) which correctly requires the stronger isAdministrator() (requiring rol_administrator=true) and the contacts_show_all system setting. A user manager who is not a full administrator can directly request contacts_data.php?mem_show_filter=3 to retrieve all user records across all organizations in the Admidio instance, bypassing multi-tenant organization isolation. This issue has been patched in version 5.0.9.

01

cvss

Technical severity, 0–10. One row per scoring source.

4.9
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.14.9mediumCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:Nsecondary · ENISA EU
v3.14.9mediumCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:Nsecondary · security-advisories@github.comexploitability 1.2 · impact 3.6
v3.14.9mediumCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:Nsecondary · OSV
v3.14.5mediumCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N/E:Usecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Trackcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (2)

Products and version ranges known to be affected.

05

advisories (1)

Vendor and ecosystem advisories describing this.

  • GHSA-g8p8-94f2-28grAdmidio Exposes Cross-Organization Member Data via Permission Check Mismatch in contacts_data.php2026-04-29ghsa · osvUS

    ## Summary The `contacts_data.php` endpoint uses a weaker permission check (`isAdministratorUsers()`, requiring only `rol_edit_user=true`) than the frontend UI (`contacts.php`) which correctly requires the stronger `isAdministrator()` (requiring `rol_administrator=true`) and the `contacts_show_all` system setting. A user manager who is not a full administrator can directly request `contacts_data.php?mem_show_filter=3` to retrieve all user records across all organizations in the Admidio instance, bypassing multi-tenant organization isolation. ## Details The frontend page `contacts.php` and the backend data endpoint `contacts_data.php` have mismatched authorization checks for the "show all organizations" filter (`mem_show_filter=3`). **Frontend guard** at `modules/contacts/contacts.php:80`: ```php if ($gCurrentUser->isAdministrator() && $gSettingsManager->getBool('contacts_show_all')) { // Only then is filter=3 ("All Organizations") shown in the dropdown $selectBoxValues = array( ... '3' => array('3', $gL10n->get('SYS_ALL_CONTACTS'), $gL10n->get('SYS_ALL_ORGANIZATIONS')) ); } ``` This correctly requires both `isAdministrator()` (`rol_administrator=true`) AND the `contacts_show_all` setting. **Backend check** at `modules/contacts/contacts_data.php:235`: ```php } elseif (($getMembersShowFilter === 3) && $gCurrentUser->isAdministratorUsers()) { $mainSql = $contactsListConfig->getSql( array( 'showAllMembersDatabase' => true, ... ) ); ``` This only requires `isAdministratorUsers()` which checks `rol_edit_user=true` — a weaker permission available to non-admin "user manager" roles. The `contacts_show_all` setting is never checked. **The critical difference between the two methods** (from `src/Users/Entity/User.php`): - `isAdministrator()` (line 1507): checks the `rol_administrator` flag — full system administrator - `isAdministratorUsers()` (line 1625): checks `rol_edit_user` right — user management module access only When `showAllMembersDatabase=true` reaches `ListConfiguration::getSql()` (at `src/Roles/Entity/ListConfiguration.php:1022-1028`), the generated SQL removes ALL organization filtering: ```php } elseif ($optionsAll['showAllMembersDatabase']) { $sql = 'SELECT DISTINCT ' . $sqlMemLeader . $sqlIdColumns . $sqlColumnNames . ' FROM ' . TBL_USERS . ' ' . $sqlJoin . ' WHERE usr_valid = true ' . $sqlWhere . $sqlOrderBys; } ``` Compare with the default query which includes `cat_org_id = $gCurrentOrgId` to restrict results to the current organization. The cross-org indicator subqueries at line 169 do correctly check `isAdministrator()`, so the `member_other_orga` columns return 0 — but this only affects display indicators, not the actual user data returned. ## PoC **Prerequisites:** An Admidio instance with at least two organizations sharing the same database. A user account in Organization A assigned to a role with `rol_edit_user=1` but `rol_administrator=0`. **Step 1:** Log in as the user manager account and capture the session cookie. **Step 2:** Request all users across all organizations by directly calling the data endpoint: ```bash curl -s -b 'PHPSESSID=<user_manager_session>' \ 'https://target/adm_program/modules/contacts/contacts_data.php?mem_show_filter=3&draw=1&start=0&length=100&search%5Bvalue%5D=' ``` **Expected behavior:** The request should be rejected or return only current-organization users, since the user is not a full administrator and the frontend never offers filter=3 to non-administrators. **Actual behavior:** The endpoint returns a JSON response containing all users from ALL organizations in the database, including: - User UUIDs (`usr_uuid`) - Login names (`login_name`) - Email addresses (`member_email`) - All configured profile fields (names, addresses, phone numbers, etc.) **Step 3:** Verify that users from Organization B (where the attacker has no membership) appear in the results by checking the `member_this_orga` field — it will be `0` for cross-org users. ## Impact In multi-organization Admidio deployments (the primary use case for organization isolation), a user manager in one organization can exfiltrate the complete member directory of all other organizations sharing the same database. Exposed data includes: - Full names and all configured profile fields - Email addresses - Login names (useful for credential attacks) - User UUIDs (useful for targeting other API endpoints) This completely bypasses the multi-tenant organization isolation boundary. The `contacts_show_all` admin setting (intended to control this feature) is also bypassed, meaning even instances where administrators have explicitly disabled cross-org viewing are affected. ## Recommended Fix Change line 235 in `modules/contacts/contacts_data.php` to match the frontend guard at `contacts.php:80`: ```php // Before (vulnerable): } elseif (($getMembersShowFilter === 3) && $gCurrentUser->isAdministratorUsers()) { // After (fixed): } elseif (($getMembersShowFilter === 3) && $gCurrentUser->isAdministrator() && $gSettingsManager->getBool('contacts_show_all')) { ``` Additionally, as defense-in-depth, add an early rejection at the top of the file (after line 59) to block the filter value entirely for unauthorized users: ```php if ($getMembersShowFilter === 3 && (!$gCurrentUser->isAdministrator() || !$gSettingsManager->getBool('contacts_show_all'))) { $getMembersShowFilter = 0; // Fall back to default } ```

06

also known as (3)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-05-07modified 2026-06-17
08

references (6)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan