Wicked Folders <= 4.1.0 - Insecure Direct Object Reference to Authenticated (Contributor+) Arbitrary Folder Deletion
Description
The Wicked Folders – Folder Organizer for Pages, Posts, and Custom Post Types plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 4.1.0 via the delete_folders() function due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Contributor-level access and above, to delete arbitrary folders created by other users.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.1.0What Changed in the Fix
Changes introduced in v4.1.1
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-1883 - Wicked Folders IDOR Folder Deletion ## 1. Vulnerability Summary The **Wicked Folders** plugin (<= 4.1.0) is vulnerable to an **Insecure Direct Object Reference (IDOR)** in the `Folder_API::delete_folders` REST API endpoint. The vulnerability exists beca…
Show full research plan
Exploitation Research Plan: CVE-2026-1883 - Wicked Folders IDOR Folder Deletion
1. Vulnerability Summary
The Wicked Folders plugin (<= 4.1.0) is vulnerable to an Insecure Direct Object Reference (IDOR) in the Folder_API::delete_folders REST API endpoint. The vulnerability exists because the plugin fails to validate whether the authenticated user (Contributor or higher) has permission to delete the specific folder IDs provided in the request. While a permission check ensures the user can manage folders for a given post_type, it does not enforce ownership or access control at the individual folder level, allowing a low-privileged user to delete folders created by an Administrator.
2. Attack Vector Analysis
- Endpoint:
DELETE /wp-json/wicked-folders/v1/folders - Authentication: Authenticated (Contributor+)
- Capability Required: Typically
edit_posts(inherent to Contributor). - Vulnerable Parameter:
folder_ids(an array of folder IDs to be deleted). - Required Parameters:
post_type: The post type the folders belong to (e.g.,post,page).folder_ids: An array of numeric IDs of the folders to delete.
3. Code Flow
- Entry Point: The REST API route is registered in
classes/rest-api/v1/folder-api.php:register_rest_route( $this->base, '/folders', array( array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_folders' ), 'permission_callback' => array( $this, 'delete_folders_permissions_check' ), 'args' => array( 'post_type' => array( 'required' => true ), 'folder_ids' => array( 'required' => true, 'default' => array() ), // ... ), ), )); - Permission Check:
delete_folders_permissions_check(not fully shown in source, but inferred) checks if the user has general permission to edit folders for the specifiedpost_type. Contributor-level users pass this check by default for post types they can edit. - Vulnerable Sink: The callback
delete_folders(infolder-api.php) instantiates aFolder_Collectionfor the givenpost_typeand calls thedeletemethod:// From classes/folder-collection.php public function delete( $ids ) { // ... (logic for parent reassignment) foreach ( $this->items as $folder ) { if ( in_array( $folder->id, $ids ) ) { // No check if $folder belongs to the current user $folder->delete(); $this->remove( $folder ); } } return $changed; } - Vulnerability: The loop in
Folder_Collection::deleteiterates through all folders associated with thepost_type. If an ID matches the user-provided$idsarray, it calls$folder->delete(). There is no check to ensure the folder was created by the current user or that the user has specific rights to that folder.
4. Nonce Acquisition Strategy
The REST API endpoint requires a standard WordPress REST Nonce (wp_rest).
- Login: Log in as a Contributor user.
- Navigate: Use
browser_navigateto go to the WordPress Admin dashboard (e.g.,/wp-admin/edit.php). - Extract Nonce: WordPress exposes the REST nonce in the
wpApiSettingsobject.- Tool:
browser_eval - Command:
browser_eval("wpApiSettings.nonce")
- Tool:
- Fallback: If
wpApiSettingsis missing, checkwickedFoldersSettingsor similar localization objects enqueued by the plugin in the page source.
5. Exploitation Strategy
Step-by-Step Plan:
- Identify Target: As an Administrator, create a folder for the
postpost type and note its ID. - Setup Attacker: Create/login as a Contributor user.
- Obtain Nonce: Extract the
X-WP-Nonceas described in Section 4. - Send Malicious Request:
- Action: Send a
DELETErequest to the REST API. - Endpoint:
/wp-json/wicked-folders/v1/folders - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Payload:
{ "post_type": "post", "folder_ids": [TARGET_FOLDER_ID] }
- Action: Send a
- Tool: Use
http_request(Playwright) to send the DELETE request.
6. Test Data Setup
- Target Folder (Admin):
- Create a folder using the plugin's UI or via WP-CLI:
wp term create wf_folder "Sensitive Admin Folder" --description="post" - Note the ID (e.g.,
15).
- Create a folder using the plugin's UI or via WP-CLI:
- Attacker User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123
7. Expected Results
- Response: The server should return a
200 OKor204 No Contentresponse, potentially containing the updated folder collection. - Side Effect: The folder with ID
TARGET_FOLDER_ID(created by Admin) will be deleted from the database.
8. Verification Steps
- Check Database: Use WP-CLI to check if the term still exists in the
wf_foldertaxonomy.wp term list wf_folder --fields=term_id,name
- Verify Deletion: If the
TARGET_FOLDER_IDis no longer in the list, the exploit was successful.
9. Alternative Approaches
- Single Folder Endpoint: If the bulk delete fails, try the single folder delete endpoint:
DELETE /wp-json/wicked-folders/v1/folders/(?P<id>[\d]+)- Payload:
{"post_type": "post"}
- Payload:
- Post Type Variation: Test with
post_type=pageor any other custom post type supported by the plugin, as the vulnerability resides in the coreFolder_Collectionlogic used across types.
Summary
The Wicked Folders plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) due to missing validation on user-controlled folder IDs in the REST API. This allows authenticated attackers with Contributor-level access or higher to delete arbitrary folders created by other users, including administrators.
Vulnerable Code
// classes/rest-api/v1/folder-api.php public function delete_folder_permissions_check( $request ) { $allowed = current_user_can( 'edit_posts' ); $user_id = get_current_user_id(); $term_id = $request->get_param( 'id' ); $post_type = $request->get_param( 'postType' ); $taxonomy = Wicked_Folders::get_tax_name( $post_type ); return apply_filters( 'wicked_folders_can_delete_folder', $allowed, $user_id, $term_id, $taxonomy ); } public function delete_folders_permissions_check( $request ) { return current_user_can( 'edit_posts' ); } --- // classes/folder-collection.php public function delete( $ids ) { $changed = new Folder_Collection(); // ... (truncated reassignment logic) // Now we can delete foreach ( $this->items as $folder ) { if ( in_array( $folder->id, $ids ) ) { $folder->delete(); $this->remove( $folder ); } } return $changed; }
Security Fix
@@ -68,6 +68,17 @@ if ( $folder->id === $item->id ) { unset( $this->items[ $index ] ); + // Re-index. I don't understand why this is needed but leaving it out + // can prevent looping over the collection sometimes + $this->items = array_values( $this->items ); + + // Adjust index if the removed item is the same or before the current index. + // Setting to -1 is okay because the next() method will increment it (although + // then current() will be wrong so perhaps this needs to be implemented differently?) + if ( $index <= $this->index ) { + $this->index--; + } + break; } } @@ -277,27 +277,41 @@ } public function update_folder_permissions_check( $request ) { - $allowed = current_user_can( 'edit_posts' ); $user_id = get_current_user_id(); $term_id = $request->get_param( 'id' ); $post_type = $request->get_param( 'postType' ); $taxonomy = Wicked_Folders::get_tax_name( $post_type ); + $allowed = current_user_can( 'edit_term', $term_id ); return apply_filters( 'wicked_folders_can_edit_folder', $allowed, $user_id, $term_id, $taxonomy ); } public function delete_folder_permissions_check( $request ) { - $allowed = current_user_can( 'edit_posts' ); $user_id = get_current_user_id(); $term_id = $request->get_param( 'id' ); $post_type = $request->get_param( 'postType' ); $taxonomy = Wicked_Folders::get_tax_name( $post_type ); + $allowed = current_user_can( 'delete_term', $term_id ); return apply_filters( 'wicked_folders_can_delete_folder', $allowed, $user_id, $term_id, $taxonomy ); } public function delete_folders_permissions_check( $request ) { - return current_user_can( 'edit_posts' ); + $user_id = get_current_user_id(); + $post_type = $request->get_param( 'post_type' ); + $folder_ids = $request->get_param( 'folder_ids' ); + $taxonomy = Wicked_Folders::get_tax_name( $post_type ); + + foreach ( $folder_ids as $term_id ) { + $allowed = current_user_can( 'delete_term', $term_id ); + $allowed = apply_filters( 'wicked_folders_can_delete_folder', $allowed, $user_id, $term_id, $taxonomy ); + + if ( ! $allowed ) { + return false; + } + } + + return true; }
Exploit Outline
1. Login to the WordPress site as an authenticated user with at least Contributor-level permissions. 2. Identify the target folder IDs (numeric values corresponding to the custom taxonomy terms used by the plugin) that were created by a different user (e.g., an Administrator). 3. Extract a valid WordPress REST API nonce (`X-WP-Nonce`) from the administrative dashboard source code (e.g., from the `wpApiSettings` JavaScript object). 4. Send a `DELETE` request to the `/wp-json/wicked-folders/v1/folders` endpoint. 5. Include a JSON payload containing the `post_type` (e.g., 'post' or 'page') and a `folder_ids` array containing the target IDs to be deleted. 6. Because the plugin only checks for the general `edit_posts` capability and not specific ownership or `delete_term` permissions for the provided IDs, the folders will be deleted successfully.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.