[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fuGx_Fc_TUiVaHWoX7Zd-yqk5I55myCy1AEgNL4TugfA":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2026-2732","enable-media-replace-improper-authorization-to-authenticated-author-arbitrary-attachment-change-via-background-replace","Enable Media Replace \u003C= 4.1.7 - Improper Authorization to Authenticated (Author+) Arbitrary Attachment Change via Background Replace","The Enable Media Replace plugin for WordPress is vulnerable to unauthorized modification of data due to an improper capability check on the 'RemoveBackGroundViewController::load' function in all versions up to, and including, 4.1.7. This makes it possible for authenticated attackers, with Author-level access and above, to replace any attachment with a removed background attachment.","enable-media-replace",null,"\u003C=4.1.7","4.1.8","medium",5.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:L","Missing Authorization","2026-03-03 18:17:08","2026-03-04 06:26:53",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F2c5f2dc8-67f7-4dbf-8631-f434522f1b53?source=api-prod",1,[22,23,24,25,26,27,28,29],"build\u002Fshortpixel\u002Ffilesystem\u002Fsrc\u002FModel\u002FFile\u002FFileModel.php","build\u002Fshortpixel\u002Flog\u002Fsrc\u002FDebugItem.php","build\u002Fshortpixel\u002Fnotices\u002Fsrc\u002FNoticeModel.php","classes\u002FController\u002FReplaceController.php","classes\u002FViewController\u002FRemoveBackgroundViewController.php","classes\u002FViewController\u002FUploadViewController.php","classes\u002Femr-plugin.php","classes\u002Fexternal\u002Fupgrader_skin.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-2732\n\n## 1. Vulnerability Summary\nThe **Enable Media Replace** plugin for WordPress is vulnerable to improper authorization in its \"Background Replace\" feature. Specifically, the `EnableMediaReplace\\ViewController\\RemoveBackGroundViewController::load` and `loadPost` functions do not verify if the current user has the authority to modify a specific attachment. \n\nWhile the code checks for the `upload_files` capability (held by Authors and above), it fails to check the `edit_post` capability for the specific `attachment_id` being manipulated. This allows an authenticated user with Author-level access to replace any attachment on the site (including those owned by Administrators) with an image processed through the background removal service.\n\n## 2. Attack Vector Analysis\n- **Endpoint**: `wp-admin\u002Fupload.php?page=enable-media-replace\u002Fenable-media-replace.php`\n- **Action**: `emr_prepare_remove` (to get the nonce\u002FUI) and the subsequent POST request handled by `loadPost`.\n- **Vulnerable Params**: `ID` (The attachment ID to be replaced) and `key` (The identifier for the processed image).\n- **Authentication**: Authenticated, Author-level role (capability `upload_files`).\n- **Preconditions**:\n    1. A target attachment ID owned by an Administrator must exist.\n    2. The attacker must have at least one attachment of their own to generate a valid `key` and `nonce` from the background removal flow.\n\n## 3. Code Flow\n1. **Entry Point**: The plugin registers a submenu page in `classes\u002Femr-plugin.php`:\n   ```php\n   add_submenu_page('upload.php', $title, $title, 'upload_files', 'enable-media-replace\u002Fenable-media-replace.php', array($this, 'route'));\n   ```\n2. **Routing**: The `route` method determines which `ViewController` to load. For background removal, it invokes `RemoveBackGroundViewController`.\n3. **Missing Permission Check (GET)**: In `classes\u002FViewController\u002FRemoveBackgroundViewController.php`, the `load()` function:\n   ```php\n   public function load() {\n       if (!current_user_can('upload_files')) { \u002F\u002F Author+ check\n           $this->viewError(self::ERROR_UPLOAD_PERMISSION);\n       }\n       $attachment_id = intval($_REQUEST['attachment_id']); \u002F\u002F No check if user can edit this ID\n       \u002F\u002F ...\n   }\n   ```\n4. **Processing (POST)**: When the \"Replace\" button is clicked after background removal, `loadPost()` is called:\n   ```php\n   public function loadPost() {\n       if (!isset($_POST['emr_nonce']) || !wp_verify_nonce($_POST['emr_nonce'], 'media_remove_background')) {\n           $this->viewError(self::ERROR_NONCE);\n       }\n       $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; \u002F\u002F Target ID from user input\n       $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : null; \u002F\u002F Result key from API\n       \n       $result = $this->replaceBackground($post_id, $key); \u002F\u002F Downloads processed image\n       $replaceController = new ReplaceController($post_id);\n       \u002F\u002F ... sets up params ...\n       $result = $replaceController->run(); \u002F\u002F Overwrites target attachment\n   }\n   ```\n   At no point in `loadPost` is there a check like `current_user_can('edit_post', $post_id)`.\n\n## 4. Nonce Acquisition Strategy\nThe nonce `media_remove_background` is required for the `loadPost` action.\n1. **Target Identification**: Find an `attachment_id` owned by the Admin.\n2. **Nonce Extraction**:\n   - Create a dummy attachment owned by the Author.\n   - Navigate to the background removal preparation page for the Author's *own* image:\n     `wp-admin\u002Fupload.php?page=enable-media-replace\u002Fenable-media-replace.php&action=emr_prepare_remove&attachment_id=[AUTHOR_ID]`\n   - The nonce is generated in the view `prepare-remove-background`.\n   - Use `browser_eval` to extract it:\n     ```javascript\n     document.querySelector('input[name=\"emr_nonce\"]').value\n     ```\n3. **Key Acquisition**:\n   - The `key` identifies the background-removed image processed by the ShortPixel API. \n   - The attacker can initiate a legitimate removal on their own image. When the API returns the result, the `key` is populated in the form. This `key` can then be repurposed against the Admin's `ID`.\n\n## 5. Exploitation Strategy\n1. **Login** as a user with the `Author` role.\n2. **Prepare Target**: Identify an Admin-owned attachment (e.g., ID `10`).\n3. **Prepare Payload**: Upload a simple image as Author (e.g., ID `11`).\n4. **Initiate Process**: Start the \"Remove Background\" flow for ID `11`.\n5. **Intercept\u002FCapture**: Capture the `emr_nonce` and the `key` returned by the ShortPixel background removal service. (The `key` is typically returned via AJAX or updated in the UI after the image is processed).\n6. **Execute Exploit**: Submit a POST request with the captured `key` but pointing to the Admin's attachment `ID`.\n\n**Request Details**:\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fupload.php?page=enable-media-replace%2Fenable-media-replace.php`\n- **Method**: POST\n- **Headers**: `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body**:\n  ```\n  emr_nonce=[CAPTURED_NONCE]&\n  ID=10&\n  key=[CAPTURED_KEY]&\n  action=emr_remove_background\n  ```\n  *(Note: The exact `action` parameter may vary depending on how the plugin's router handles the `loadPost` transition, but typically it follows the submenu page logic)*.\n\n## 6. Test Data Setup\n1. **Users**: \n   - `admin_user` (Administrator)\n   - `author_user` (Author)\n2. **Attachments**:\n   - `admin_img.jpg` uploaded by `admin_user` (Target ID).\n   - `author_img.jpg` uploaded by `author_user` (Attacker-controlled source).\n\n## 7. Expected Results\n- The plugin will successfully process the request because the Author has `upload_files` capability.\n- The `ReplaceController` will fetch the image associated with the `key` (which the Author generated) and overwrite the file associated with the Admin's attachment `ID`.\n- The Admin's image is now replaced by the Author's background-removed image.\n\n## 8. Verification Steps\n1. **Check Attachment Owner**: \n   `wp post get [ADMIN_ID] --field=post_author` (Should still be Admin).\n2. **Check File Path**:\n   `wp post get [ADMIN_ID] --field=guid`\n3. **Verify File Content**: Check if the file on disk for the Admin's attachment has changed to the background-removed version of the Author's image.\n4. **Check Meta**:\n   `wp post meta get [ADMIN_ID] _emr_replace_author` \n   *(In `ReplaceController::run()`, the plugin explicitly tracks the replacer: `update_post_meta($this->post_id, '_emr_replace_author', get_current_user_id());`. This meta will confirm the Author ID replaced it.)*\n\n## 9. Alternative Approaches\nIf the `key` is tied strictly to the original `attachment_id` within the API side, the attacker can try to manipulate the `ID` parameter during the *initial* request to the background removal service. However, the `loadPost` logic in the plugin suggests it accepts any `ID` alongside a valid `key` and `nonce`. \n\nIf the background removal API is unavailable, the exploit can be proven by mocking the `replaceBackground` result to return a local path, demonstrating that the `ReplaceController` proceeds to overwrite any `ID` without checking if the user is the owner.","The Enable Media Replace plugin for WordPress (\u003C= 4.1.7) fails to perform granular authorization checks in its background removal feature. Authenticated users with Author-level access can replace any attachment on the site—including those owned by administrators—by manipulating the attachment ID in the background replacement workflow.","\u002F\u002F classes\u002FViewController\u002FRemoveBackgroundViewController.php line 40\n\tpublic function load()\n\t{\n\t if (!current_user_can('upload_files')) {\n\t\t\t $this->viewError(self::ERROR_UPLOAD_PERMISSION);\n\t\t\t\u002F\u002F wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace'));\n\t }\n\n\n\t $attachment_id = intval($_REQUEST['attachment_id']);\n\n---\n\n\u002F\u002F classes\u002FViewController\u002FRemoveBackgroundViewController.php line 86\n\t\t $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; \u002F\u002F sanitize, post_id.\n\t\t if (is_null($post_id)) {\n\t\t\t \t $this->viewError(self::ERROR_FORM);\n\u002F\u002F\t\t     wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));\n\t\t }\n\n\t\t $this->setView($post_id);\n\t\t $result = $this->replaceBackground($post_id, $key);\n\n\t\t if (false === $result->success)\n\t\t {\n\t\t\t  $this->view->errorMessage = $result->message;\n\t\t\t\t$this->viewError();\n\t\t }\n\t\t elseif (! file_exists($result->image))\n\t\t {\n\t\t\t $this->viewError(self::ERROR_DOWNLOAD_FAILED);\n\t\t }\n\n\u002F\u002F\t\t $result = $replacer->replaceWith($result->image, $source->getFileName() , true);\n\u002F\u002F$params = array();\n\t\t$replaceController = new ReplaceController($post_id);\n\t\t$sourceFile = $replaceController->getSourceFile();","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fenable-media-replace\u002F4.1.7\u002Fclasses\u002FViewController\u002FRemoveBackgroundViewController.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fenable-media-replace\u002F4.1.8\u002Fclasses\u002FViewController\u002FRemoveBackgroundViewController.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fenable-media-replace\u002F4.1.7\u002Fclasses\u002FViewController\u002FRemoveBackgroundViewController.php\t2023-09-14 11:51:00.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fenable-media-replace\u002F4.1.8\u002Fclasses\u002FViewController\u002FRemoveBackgroundViewController.php\t2026-03-03 10:21:48.000000000 +0000\n@@ -37,10 +37,15 @@\n \t\t\t\u002F\u002F wp_die(esc_html__('You do not have permission to upload files.', 'enable-media-replace'));\n \t }\n \n-\n \t $attachment_id = intval($_REQUEST['attachment_id']);\n \t $attachment = get_post($attachment_id);\n \n+\t if (! \\emr()->checkImagePermission($attachment))\n+\t {\n+\t\t $this->viewError(self::ERROR_IMAGE_PERMISSION);\n+\t   wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') );\n+\t }\n+\n \t $uiHelper = \\emr()->uiHelper();\n \t $uiHelper->setPreviewSizes();\n \t $uiHelper->setSourceSizes($attachment_id);\n@@ -77,30 +82,34 @@\n \t\t if (is_null($key) || strlen($key) == 0)\n \t\t {\n \t\t\t $this->viewError(self::ERROR_KEY);\n-\t\t\t \u002F\u002Fwp_die(esc_html__('Error while sending form (no key). Please try again.', 'enable-media-replace'));\n \t\t }\n \n \t\t $post_id = isset($_POST['ID']) ? intval($_POST['ID']) : null; \u002F\u002F sanitize, post_id.\n \t\t if (is_null($post_id)) {\n \t\t\t \t $this->viewError(self::ERROR_FORM);\n-\u002F\u002F\t\t     wp_die(esc_html__('Error in request. Please try again', 'enable-media-replace'));\n \t\t }\n \n+\t\t $attachment = get_post($post_id);\n+\n+\t\t if (! \\emr()->checkImagePermission($attachment))\n+\t\t {\n+\t\t\t $this->viewError(self::ERROR_IMAGE_PERMISSION);\n+\t\t   wp_die( esc_html__('You do not have permission to upload files for this author.', 'enable-media-replace') );\n+\t\t }\t\t \n+\n \t\t $this->setView($post_id);\n \t\t $result = $this->replaceBackground($post_id, $key);\n \n \t\t if (false === $result->success)\n \t\t {\n \t\t\t  $this->view->errorMessage = $result->message;\n-\t\t\t\t$this->viewError();\n+\t\t\t\t$this->viewError(self::ERROR_DOWNLOAD_FAILED);\n \t\t }\n \t\t elseif (! file_exists($result->image))\n \t\t {\n \t\t\t $this->viewError(self::ERROR_DOWNLOAD_FAILED);\n \t\t }\n \n-\u002F\u002F\t\t $result = $replacer->replaceWith($result->image, $source->getFileName() , true);\n-\u002F\u002F$params = array();\n \t\t$replaceController = new ReplaceController($post_id);\n \t\t$sourceFile = $replaceController->getSourceFile();","The exploit relies on the fact that the background replacement endpoint only checks for the `upload_files` capability (Author+) but not if the user can edit the specific attachment ID. \n\n1. Log in as a user with the Author role.\n2. Create a dummy attachment (Author-owned) and initiate the \"Remove Background\" process to generate a valid `media_remove_background` nonce and a ShortPixel API `key` representing the processed image.\n3. Identify the target `attachment_id` (e.g., a file owned by the Admin).\n4. Send a POST request to the plugin's background removal handler (`wp-admin\u002Fupload.php?page=enable-media-replace\u002Fenable-media-replace.php`) with the following parameters:\n    - `ID`: The target attachment ID (Admin's image).\n    - `key`: The captured API key from the Author's processed image.\n    - `emr_nonce`: The captured valid nonce.\n    - `action`: `emr_remove_background` (or equivalent routing parameter).\n5. The plugin will download the background-removed image and overwrite the target Admin file because it fails to call `current_user_can('edit_post', $post_id)`.","gemini-3-flash-preview","2026-04-18 06:40:59","2026-04-18 06:41:26",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","4.1.7","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fenable-media-replace\u002Ftags\u002F4.1.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fenable-media-replace.4.1.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fenable-media-replace\u002Ftags\u002F4.1.8","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fenable-media-replace.4.1.8.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fenable-media-replace\u002Ftags"]