CVE-2025-12640

Folders – Unlimited Folders to Organize Media Library Folder, Pages, Posts, File Manager <= 3.1.5 - Missing Authorization to Authenticated (Author+) Media Replacement

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.1.6
Patched in
1d
Time to patch

Description

The Folders – Unlimited Folders to Organize Media Library Folder, Pages, Posts, File Manager plugin for WordPress is vulnerable to Unauthorized Arbitrary Media Replacement in all versions up to, and including, 3.1.5. This is due to missing object-level authorization checks in the handle_folders_file_upload() function. This makes it possible for authenticated attackers, with Author-level access and above, to replace arbitrary media files from the WordPress Media Library.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.1.5
PublishedJanuary 7, 2026
Last updatedJanuary 8, 2026
Affected pluginfolders

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-12640 (Folders Plugin) ## 1. Vulnerability Summary The **Folders** plugin (up to v3.1.5) contains a missing object-level authorization (IDOR) vulnerability in the `handle_folders_file_upload()` function. While the plugin may check if a user has a general capab…

Show full research plan

Exploitation Research Plan: CVE-2025-12640 (Folders Plugin)

1. Vulnerability Summary

The Folders plugin (up to v3.1.5) contains a missing object-level authorization (IDOR) vulnerability in the handle_folders_file_upload() function. While the plugin may check if a user has a general capability to upload files (e.g., upload_files), it fails to verify if the authenticated user has permission to modify the specific media attachment ID provided in the request. This allows an attacker with Author level permissions or higher to replace media files owned by other users, including Administrators.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: folders_file_upload (inferred from function name)
  • HTTP Method: POST
  • Authentication: Required (Author level or higher).
  • Vulnerable Parameter: attachment_id (or similar ID field) and the file / async-upload payload.
  • Preconditions:
    • An attacker must have credentials for an account with at least Author role.
    • The attacker must know the attachment ID of the media file they wish to replace.

3. Code Flow (Inferred)

  1. Registration: The plugin registers an AJAX handler:
    add_action('wp_ajax_folders_file_upload', array($this, 'handle_folders_file_upload'));
  2. Handler Entry: The handle_folders_file_upload() function is called.
  3. Basic Check: The function likely calls check_ajax_referer() to validate a CSRF nonce and current_user_can('upload_files').
  4. Vulnerable Logic: The function accepts an attachment_id from $_POST.
  5. The Sink: It uses the provided ID to locate the file path on disk and proceeds to overwrite the existing file with the newly uploaded file content using move_uploaded_file() or WP_Filesystem, without checking if current_user_can('edit_post', $attachment_id).

4. Nonce Acquisition Strategy

The folders_file_upload action likely requires a nonce. In the Folders plugin, nonces are usually localized for the media library interface.

  1. Identify the Script: Grep the plugin for wp_localize_script to find where the nonce is passed to the frontend.
    • Command: grep -rn "wp_localize_script" wp-content/plugins/folders/
  2. Locate Nonce Key: Look for a key related to "upload" or "nonce" (e.g., folders_nonce or upload_nonce).
  3. Extraction via Browser:
    • The execution agent should log in as the Author user.
    • Navigate to the Media Library: browser_navigate("http://localhost:8080/wp-admin/upload.php").
    • Use browser_eval to extract the nonce.
    • Example (inferred): browser_eval("window.folders_data?.nonce") or browser_eval("window.folders_settings?.upload_nonce").

5. Exploitation Strategy

The goal is to replace a sensitive file (e.g., company-logo.png uploaded by an Admin) with a malicious or spoofed image.

  1. Preparation: Log in as the Author user using the http_request tool to obtain session cookies.
  2. Information Gathering: Identify the attachment_id of a target image uploaded by the Admin (via wp media list in the CLI).
  3. Craft the Request:
    • URL: http://localhost:8080/wp-admin/admin-ajax.php
    • Headers: Content-Type: multipart/form-data
    • Parameters:
      • action: folders_file_upload (inferred)
      • nonce: [EXTRACTED_NONCE]
      • attachment_id: [TARGET_ID]
      • file: (The new image file binary)
  4. Execute: Send the POST request using the http_request tool.

6. Test Data Setup

  1. Target User: Use the existing admin user.
  2. Attacker User: Create an Author user:
    • wp user create attacker attacker@example.com --role=author --user_pass=password123
  3. Target Media: As admin, upload a specific file:
    • wp media import /path/to/original-image.png --title="Admin Secret Image" --user=admin
  4. Note the ID: Get the ID of the new media:
    • wp post list --post_type=attachment

7. Expected Results

  • The HTTP response should indicate success (e.g., {"success": true} or a 200 OK with file metadata).
  • The file on the server associated with the Admin's attachment_id will now contain the data from the Author's uploaded file.
  • The original image is overwritten.

8. Verification Steps

  1. Check File Content: Use WP-CLI to find the path of the target attachment and verify its integrity.
    • wp post get [ID] --field=guid (to get the URL)
  2. Verify Overwrite: Check the file on disk to see if it matches the attacker's payload rather than the original.
    • md5sum wp-content/uploads/YYYY/MM/original-image.png
  3. Confirm Permissions: Use wp post get [ID] to confirm the post_author is still the Admin (ID 1), proving an Author modified an Admin's resource.

9. Alternative Approaches

  • Search for Other Actions: If folders_file_upload is incorrect, grep for the function name handle_folders_file_upload in the plugin directory to find the actual wp_ajax_ string.
  • REST API: Check if the plugin registers any REST routes in rest_api_init hooks that map to the same file upload logic, as these may also lack the permission_callback object check.
  • Directory Traversal: If the attachment_id logic involves building a path, test if the ID can be manipulated or if other path parameters allow overwriting files outside the uploads directory (though less likely for this specific CVE).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Folders plugin for WordPress is vulnerable to unauthorized arbitrary media replacement due to a missing object-level authorization check in the handle_folders_file_upload() function. This allows authenticated attackers with Author-level permissions or higher to overwrite any media file in the library by supplying its attachment ID, regardless of who owns the file.

Vulnerable Code

// In the Folders plugin, handle_folders_file_upload handles file replacement via AJAX
// Located in the primary plugin file or AJAX handler file (e.g., wp-content/plugins/folders/includes/folders.php)

public function handle_folders_file_upload() {
    check_ajax_referer('folders_nonce', 'nonce');

    // The function checks for general upload capabilities
    if (!current_user_can('upload_files')) {
        wp_send_json_error('Unauthorized');
    }

    // It retrieves the attachment_id to be replaced from POST data
    $attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : 0;

    // VULNERABILITY: Missing check to see if the user has permission to edit the specific post ID
    // e.g., if (!current_user_can('edit_post', $attachment_id)) { ... }

    // ... (truncated: logic to handle the file upload and replace the existing media file path) ...
}

Security Fix

--- a/wp-content/plugins/folders/includes/folders.php
+++ b/wp-content/plugins/folders/includes/folders.php
@@ -100,6 +100,10 @@
         if (!current_user_can('upload_files')) {
             wp_send_json_error(array('message' => 'Unauthorized'));
         }
+
+        if (!current_user_can('edit_post', $attachment_id)) {
+            wp_send_json_error(array('message' => 'You do not have permission to edit this item.'));
+        }
 
         $file = $_FILES['file'];

Exploit Outline

The exploit targets the AJAX action folders_file_upload. An attacker requires an account with Author permissions or higher. First, the attacker identifies the attachment_id of a media file they wish to replace (e.g., an image uploaded by an administrator). They then obtain a valid AJAX nonce, typically exposed in the WordPress Media Library interface via wp_localize_script (often found in window.folders_settings.nonce). Finally, the attacker sends a multipart POST request to wp-admin/admin-ajax.php with the parameters action=folders_file_upload, the target attachment_id, the valid nonce, and a new file payload. Because the plugin fails to verify if the user has edit_post capabilities for the specific attachment_id, the original file is overwritten on the server.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.