[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f4DV0Jk59V5Kl8B6IIxlfH_thbgAA5d9JovEQXsJgo3o":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":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-4758","wp-job-portal-authenticated-subscriber-arbitrary-file-deletion-via-resume-custom-file-field","WP Job Portal \u003C= 2.4.9 - Authenticated (Subscriber+) Arbitrary File Deletion via Resume Custom File Field","The WP Job Portal plugin for WordPress is vulnerable to arbitrary file deletion due to insufficient file path validation in the 'WPJOBPORTALcustomfields::removeFileCustom' function in all versions up to, and including, 2.4.9. This makes it possible for authenticated attackers, with Subscriber-level access and above, to 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).","wp-job-portal",null,"\u003C=2.4.9","2.5.0","high",8.8,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:H\u002FI:H\u002FA:H","Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')","2026-03-25 10:39:46","2026-03-25 23:26:02",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fe96f31e0-4b2e-4ea1-a3e5-fd7452a2fea9?source=api-prod",1,[22,23,24,25,26,27,28],"includes\u002Factivation.php","includes\u002Fclasses\u002Fcustomfields.php","includes\u002Fclasses\u002Fuploads.php","modules\u002Fresume\u002Fmodel.php","modules\u002Fwpjobportal\u002Fmodel.php","readme.txt","wp-job-portal.php","researched",false,3,"This research plan outlines the steps required to demonstrate an arbitrary file deletion vulnerability in the **WP Job Portal** plugin (version 2.4.9). An authenticated user with Subscriber (Jobseeker) privileges can delete arbitrary files on the server by exploiting a path traversal vulnerability in the `removeFileCustom` function.\n\n---\n\n### 1. Vulnerability Summary\n*   **Vulnerability:** Authenticated Arbitrary File Deletion (Path Traversal)\n*   **Vulnerable Function:** `WPJOBPORTALcustomfields::removeFileCustom`\n*   **Location:** `includes\u002Fclasses\u002Fcustomfields.php`\n*   **Condition:** The plugin fails to validate or sanitize the file path parameter before passing it to `unlink()`. By providing a path like `..\u002F..\u002F..\u002F..\u002Fwp-config.php`, an attacker can delete sensitive system files, potentially leading to site takeover or Remote Code Execution (RCE) via reconfiguration.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **AJAX Action:** `wpjobportal_remove_custom_file` (inferred action name corresponding to the `removeFileCustom` method).\n*   **Vulnerable Parameter:** `file` (or `file_path`).\n*   **Authentication:** Required (Subscriber\u002FJobseeker role or higher).\n*   **Preconditions:** A user must be logged in as a Jobseeker. The `wpjobportal_jobseeker_controlpanel` shortcode must be active on a page to facilitate nonce acquisition.\n\n### 3. Code Flow\n1.  **Entry Point:** An authenticated user sends a POST request to `admin-ajax.php` with the action `wpjobportal_remove_custom_file`.\n2.  **AJAX Dispatch:** WordPress triggers the handler registered in the plugin, which maps to `WPJOBPORTALcustomfields::removeFileCustom`.\n3.  **Parameter Extraction:** The function retrieves the file path from the `$_POST['file']` parameter.\n4.  **Vulnerable Sink:** The function performs a check (likely `file_exists()`) and then calls `unlink($file)` on the user-supplied path without stripping directory traversal sequences (`..\u002F`).\n5.  **Result:** The file at the traversed path is deleted from the filesystem.\n\n### 4. Nonce Acquisition Strategy\nThe plugin typically enqueues a script that localizes an object containing the AJAX URL and a nonce.\n\n1.  **Identify Shortcode:** The Jobseeker control panel is rendered via the `[wpjobportal_jobseeker_controlpanel]`.\n2.  **Setup Page:** Create a page containing this shortcode.\n3.  **Navigate:** Access the page as a logged-in Subscriber.\n4.  **Extract Nonce:**\n    *   The plugin localizes the object `wpjobportal_ajax_obj`.\n    *   The nonce key is typically `nonce`.\n    *   **JS Command:** `window.wpjobportal_ajax_obj?.nonce`\n\n### 5. Exploitation Strategy\nThe agent will execute the following steps:\n\n**Step 1: Preparation**\n1.  Create a Subscriber user (Jobseeker).\n2.  Create a page with the `[wpjobportal_jobseeker_controlpanel]` shortcode using `wp post create`.\n\n**Step 2: Nonce Extraction**\n1.  Navigate to the newly created page as the Jobseeker user using `browser_navigate`.\n2.  Extract the nonce: `browser_eval(\"wpjobportal_ajax_obj.nonce\")`.\n\n**Step 3: Trigger File Deletion**\n1.  Use the `http_request` tool to send a POST request to `\u002Fwp-admin\u002Fadmin-ajax.php`.\n2.  **Payload:**\n    *   `action`: `wpjobportal_remove_custom_file`\n    *   `nonce`: [Extracted Nonce]\n    *   `file`: `..\u002F..\u002F..\u002F..\u002Fwp-config.php` (adjusting depth as necessary to reach the WordPress root from the uploads\u002Fdata directory).\n\n**Request Details:**\n*   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Method:** `POST`\n*   **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n*   **Body:** `action=wpjobportal_remove_custom_file&nonce=[NONCE]&file=..\u002F..\u002F..\u002F..\u002Fwp-config.php`\n\n### 6. Test Data Setup\n*   **WordPress User:** \n    *   Username: `jobseeker_attacker`\n    *   Role: `subscriber` (Note: Plugin may assign custom roles, but Subscriber generally has Jobseeker capabilities).\n*   **Content:**\n    *   A page with Title \"Jobseeker Panel\" and Content `[wpjobportal_jobseeker_controlpanel]`.\n*   **Target File:** Ensure `wp-config.php` exists and is writable by the web server (standard in local test environments).\n\n### 7. Expected Results\n*   **Response:** The server should return a success message or an empty response (HTTP 200).\n*   **Side Effect:** The `wp-config.php` file will be deleted from the WordPress root directory.\n\n### 8. Verification Steps\n1.  **File Existence Check:** Use `ls -la \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-config.php` via the terminal. If the file is missing, the exploit is successful.\n2.  **Site Status:** Attempt to visit the site homepage. It should trigger the WordPress installation screen (as `wp-config.php` is missing), confirming the deletion.\n\n### 9. Alternative Approaches\nIf `wpjobportal_remove_custom_file` is not the correct action name:\n1.  Search `includes\u002Fclasses\u002Fcustomfields.php` for the string `wp_ajax_` or `add_action`.\n2.  Look for any parameter named `file` or `path` within `removeFileCustom`.\n3.  Check if the file path is expected relative to a specific directory (e.g., if the plugin prepends `wp-content\u002Fuploads\u002Fwp-job-portal\u002F`, adjust the `..\u002F` depth accordingly).\n4.  Try alternative parameter names like `file_path`, `path`, or `filename`.","The WP Job Portal plugin for WordPress is vulnerable to arbitrary file deletion due to a path traversal vulnerability in the removeFileCustom function. Authenticated attackers with Subscriber-level access (Jobseekers) can exploit this to delete sensitive files like wp-config.php by providing malicious directory traversal sequences in resume custom file field parameters.","\u002F\u002F includes\u002Fclasses\u002Fcustomfields.php:1557\n    function removeFileCustom($wpjobportal_id,$wpjobportal_key,$wpjobportal_uploadfor){\n        $filename = wpjobportalphplib::wpJP_str_replace(' ', '_', $wpjobportal_key);\n        $wpjobportal_maindir = wp_upload_dir();\n        $basedir = $wpjobportal_maindir['basedir'];\n        $wpjobportal_datadirectory = wpjobportal::$_config->getConfigurationByConfigName('data_directory');\n\n        $wpjobportal_path = $basedir . '\u002F' . $wpjobportal_datadirectory. '\u002Fdata';\n\n        if($wpjobportal_uploadfor == 'company'){\n            $wpjobportal_path = $wpjobportal_path . '\u002Femployer\u002Fcomp_'.$wpjobportal_id.'\u002Fcustom_uploads';\n        }elseif($wpjobportal_uploadfor == 'job'){\n            $wpjobportal_path = $wpjobportal_path . '\u002Femployer\u002Fjob_'.$wpjobportal_id.'\u002Fcustom_uploads';\n        }elseif($wpjobportal_uploadfor == 'resume'){\n            $wpjobportal_path = $wpjobportal_path . '\u002Fjobseeker\u002Fresume_'.$wpjobportal_id.'\u002Fcustom_uploads';\n        }elseif($wpjobportal_uploadfor == 'profile'){\n            $wpjobportal_path = $wpjobportal_path . '\u002Fprofile\u002Fprofile_'.$wpjobportal_id.'\u002Fcustom_uploads';\n        }\n\n\n        $wpjobportal_userpath = $wpjobportal_path .'\u002F'.$filename;\n        wp_delete_file($wpjobportal_userpath);\n        return ;\n    }","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-job-portal\u002F2.4.9\u002Fincludes\u002Fclasses\u002Fcustomfields.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-job-portal\u002F2.5.0\u002Fincludes\u002Fclasses\u002Fcustomfields.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-job-portal\u002F2.4.9\u002Fincludes\u002Fclasses\u002Fcustomfields.php\t2026-03-18 04:33:24.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-job-portal\u002F2.5.0\u002Fincludes\u002Fclasses\u002Fcustomfields.php\t2026-03-25 04:29:28.000000000 +0000\n@@ -1557,25 +1557,45 @@\n \n     function removeFileCustom($wpjobportal_id,$wpjobportal_key,$wpjobportal_uploadfor){\n         $filename = wpjobportalphplib::wpJP_str_replace(' ', '_', $wpjobportal_key);\n-\n-        $wpjobportal_maindir = wp_upload_dir();\n-        $basedir = $wpjobportal_maindir['basedir'];\n-        $wpjobportal_datadirectory = wpjobportal::$_config->getConfigurationByConfigName('data_directory');\n-\n-        $wpjobportal_path = $basedir . '\u002F' . $wpjobportal_datadirectory. '\u002Fdata';\n-\n-        if($wpjobportal_uploadfor == 'company'){\n-            $wpjobportal_path = $wpjobportal_path . '\u002Femployer\u002Fcomp_'.$wpjobportal_id.'\u002Fcustom_uploads';\n-        }elseif($wpjobportal_uploadfor == 'job'){\n-            $wpjobportal_path = $wpjobportal_path . '\u002Femployer\u002Fjob_'.$wpjobportal_id.'\u002Fcustom_uploads';\n-        }elseif($wpjobportal_uploadfor == 'resume'){\n-            $wpjobportal_path = $wpjobportal_path . '\u002Fjobseeker\u002Fresume_'.$wpjobportal_id.'\u002Fcustom_uploads';\n-        }elseif($wpjobportal_uploadfor == 'profile'){\n-            $wpjobportal_path = $wpjobportal_path . '\u002Fprofile\u002Fprofile_'.$wpjobportal_id.'\u002Fcustom_uploads';\n-        }\n \n+        \u002F\u002F remove traversal\n+        $filename = wpjobportalphplib::wpJP_clean_file_path($filename);\n+\n+        \u002F\u002F Sanitize filename safely\n+        $filename = sanitize_file_name($filename);\n+\n+        if (empty($filename)) {\n+            return false;\n+        }\n+\n+        $filetyperesult = wp_check_filetype($filename);\n+        $wpjobportal_image_file_type = wpjobportal::$_config->getConfigurationByConfigName('image_file_type');\n+        $document_file_type = wpjobportal::$_config->getConfigurationByConfigName('document_file_type');\n+\n+        $allowed_file_types  = '';\n+        $allowed_file_types .= $document_file_type.','.$wpjobportal_image_file_type;\n+\n+        if(wpjobportalphplib::wpJP_strstr($allowed_file_types, $filetyperesult['ext'])){\n+            $wpjobportal_maindir = wp_upload_dir();\n+            $basedir = $wpjobportal_maindir['basedir'];\n+            $wpjobportal_datadirectory = wpjobportal::$_config->getConfigurationByConfigName('data_directory');\n+\n+            $wpjobportal_path = $basedir . '\u002F' . $wpjobportal_datadirectory. '\u002Fdata';\n+\n+            if($wpjobportal_uploadfor == 'company'){\n+                $wpjobportal_path = $wpjobportal_path . '\u002Femployer\u002Fcomp_'.$wpjobportal_id.'\u002Fcustom_uploads';\n+            }elseif($wpjobportal_uploadfor == 'job'){\n+                $wpjobportal_path = $wpjobportal_path . '\u002Femployer\u002Fjob_'.$wpjobportal_id.'\u002Fcustom_uploads';\n+            }elseif($wpjobportal_uploadfor == 'resume'){\n+                $wpjobportal_path = $wpjobportal_path . '\u002Fjobseeker\u002Fresume_'.$wpjobportal_id.'\u002Fcustom_uploads';\n+            }elseif($wpjobportal_uploadfor == 'profile'){\n+                $wpjobportal_path = $wpjobportal_path . '\u002Fprofile\u002Fprofile_'.$wpjobportal_id.'\u002Fcustom_uploads';\n+            }\n \n-        $wpjobportal_userpath = $wpjobportal_path .'\u002F'.$filename;\n-        wp_delete_file($wpjobportal_userpath);\n+            $wpjobportal_userpath = $wpjobportal_path .'\u002F'.$filename;\n+            wp_delete_file($wpjobportal_userpath);\n+        }\n         return ;\n     }","To exploit this vulnerability, an attacker must first authenticate as a Subscriber (Jobseeker). They need to obtain a valid nonce for the 'wpjobportal_remove_custom_file' AJAX action, which can typically be found by visiting a page containing the [wpjobportal_jobseeker_controlpanel] shortcode. Once authenticated and in possession of the nonce, the attacker sends a POST request to '\u002Fwp-admin\u002Fadmin-ajax.php' with the action set to 'wpjobportal_remove_custom_file' and a 'file' parameter containing a path traversal payload (e.g., '..\u002F..\u002F..\u002F..\u002Fwp-config.php'). The plugin processes this request and calls wp_delete_file on the resulting path, deleting the target file from the server.","gemini-3-flash-preview","2026-04-17 22:54:42","2026-04-17 22:55:19",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","2.4.9","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-job-portal\u002Ftags\u002F2.4.9","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-job-portal.2.4.9.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-job-portal\u002Ftags\u002F2.5.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-job-portal.2.5.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-job-portal\u002Ftags"]