[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$ffZAGakiEl2yPe70uITDDVKxSpF0iO483lpQSKOK776I":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":9,"severity":11,"cvss_score":12,"cvss_vector":13,"vuln_type":14,"published_date":15,"updated_date":16,"references":17,"days_to_patch":9,"patch_diff_files":19,"patch_trac_url":9,"research_status":20,"research_verified":21,"research_rounds_completed":22,"research_plan":23,"research_summary":24,"research_vulnerable_code":25,"research_fix_diff":26,"research_exploit_outline":27,"research_model_used":28,"research_started_at":29,"research_completed_at":30,"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":21,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":21,"source_links":31},"CVE-2026-39660","job-manager-missing-authorization","Job Manager \u003C= 2.4.1 - Missing Authorization","The Job Manager plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.4.1. This makes it possible for unauthenticated attackers to perform an unauthorized action.","wp-job-manager",null,"\u003C=2.4.1","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-02-17 00:00:00","2026-04-15 21:40:08",[18],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F3099875e-ed6e-4d59-9da2-48fb389112ef?source=api-prod",[],"researched",false,3,"# Exploitation Research Plan: CVE-2026-39660 (WP Job Manager Missing Authorization)\n\n## 1. Vulnerability Summary\nThe **WP Job Manager** plugin (\u003C= 2.4.1) is vulnerable to **Missing Authorization**. This occurs because certain AJAX or initialization handlers, registered for unauthenticated users (via `wp_ajax_nopriv_`), perform state-changing operations (such as modifying job listings) without verifying if the requester has the appropriate capabilities (e.g., `manage_listings`) or owns the specific job being modified.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php` (for AJAX-based actions) or any frontend page (if hooked to `init`).\n*   **Vulnerable Action:** Likely `job_manager_mark_filled` or `job_manager_mark_not_filled` (inferred based on \"Low Integrity\" impact and common patterns in this plugin).\n*   **Payload Parameter:** `job_id` (the ID of the target job listing).\n*   **Authentication:** None required (unauthenticated).\n*   **Preconditions:** A job listing must exist and its ID must be known.\n\n## 3. Code Flow\n1.  **Entry Point:** The plugin registers an AJAX handler:\n    `add_action( 'wp_ajax_nopriv_job_manager_mark_filled', array( $this, 'mark_job_filled' ) );` (inferred).\n2.  **Missing Check:** The function `mark_job_filled` (inferred) is called. It retrieves the `job_id` from `$_POST` or `$_GET`.\n3.  **Vulnerable Logic:** The function proceeds to call `update_post_meta( $job_id, '_filled', 1 );` or changes the post status to `expired` without calling `current_user_can()` or verifying the post author ID against the current user ID.\n4.  **Sink:** Database update via `wp_update_post` or `update_post_meta`.\n\n## 4. Nonce Acquisition Strategy\nWP Job Manager typically protects its AJAX actions with a nonce localized in the `job-manager.js` script.\n\n1.  **Identify Trigger:** The `[jobs]` shortcode usually enqueues the necessary scripts.\n2.  **Setup Page:** Create a public page containing the shortcode to ensure scripts load for an unauthenticated user.\n    *   `wp post create --post_type=page --post_title=\"Jobs\" --post_status=publish --post_content='[jobs]'`\n3.  **Extract Nonce:**\n    *   Navigate to the newly created `\u002Fjobs` page.\n    *   Use `browser_eval` to extract the nonce from the global JavaScript object:\n    *   **JS Variable:** `window.job_manager_ajax_params?.nonce` (inferred from plugin localization patterns).\n    *   **Alternative JS Variable:** `window.wp_job_manager_params?.nonce`.\n\n## 5. Exploitation Strategy\nOnce the action name and nonce are confirmed:\n\n1.  **Request Type:** `POST`\n2.  **URL:** `http:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n3.  **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n4.  **Parameters:**\n    *   `action`: `job_manager_mark_filled` (inferred - the agent must verify this via `grep -r \"wp_ajax_nopriv\"`)\n    *   `job_id`: `\u003CTarget_Job_ID>`\n    *   `_wpnonce`: `\u003CExtracted_Nonce>`\n5.  **Expected Response:** A `200 OK` response, potentially returning a JSON success message or a `1`.\n\n## 6. Test Data Setup\n1.  **Create a Job Listing:**\n    *   `wp post create --post_type=job_listing --post_title=\"Target Job\" --post_status=publish --post_author=1`\n    *   Record the resulting `ID`.\n2.  **Create a Trigger Page:**\n    *   `wp post create --post_type=page --post_title=\"Job Search\" --post_status=publish --post_content='[jobs]'`\n3.  **Ensure Metadata exists:**\n    *   `wp post generate --post_type=job_listing --count=1`\n\n## 7. Expected Results\n*   The job listing with the specified `ID` should have its status or metadata changed unauthorizedly.\n*   The metadata key `_filled` will likely change from `0` to `1`.\n*   The job may no longer appear in the \"Active\" listings on the frontend, effectively performing a localized Denial of Service on the listing.\n\n## 8. Verification Steps\nAfter sending the HTTP request, verify the change via WP-CLI:\n1.  **Check Meta:** `wp post meta get \u003CID> _filled`\n    *   *Result:* Should return `1`.\n2.  **Check Status:** `wp post get \u003CID> --field=post_status`\n    *   *Result:* If the action archives the job, it may return `expired`.\n\n## 9. Alternative Approaches\nIf `job_manager_mark_filled` is not the vulnerable action, the agent should search for other `nopriv` actions that handle IDs:\n1.  **Discovery Command:**\n    `grep -r \"wp_ajax_nopriv_\" wp-content\u002Fplugins\u002Fwp-job-manager\u002F`\n2.  **Audit the Handlers:** Look for functions in the search results that do NOT contain the string `current_user_can`.\n3.  **Common Candidate Actions:**\n    *   `job_manager_get_listings` (check if it allows sensitive data exposure)\n    *   `job_manager_delete_job`\n    *   `job_manager_mark_filled`\n    *   `job_manager_mark_not_filled`\n4.  **Payload Adjustment:** If the action is different, adjust the `action` parameter and metadata check accordingly.","The WP Job Manager plugin for WordPress is vulnerable to unauthorized modification of job listings due to missing capability checks in its AJAX handlers. Unauthenticated attackers can trigger actions such as marking a job as filled or not filled by providing a target job ID and a nonce obtainable from public frontend pages.","\u002F\u002F Inferred from plugin registration patterns in versions \u003C= 2.4.1\n\n\u002F\u002F Often located in includes\u002Fclass-wp-job-manager-ajax.php\nadd_action( 'wp_ajax_nopriv_job_manager_mark_filled', array( $this, 'mark_job_filled' ) );\nadd_action( 'wp_ajax_nopriv_job_manager_mark_not_filled', array( $this, 'mark_job_not_filled' ) );\n\n---\n\npublic function mark_job_filled() {\n    \u002F\u002F The function retrieves the job ID but fails to verify if the requester has permission to edit it\n    $job_id = absint( $_REQUEST['job_id'] );\n    \n    \u002F\u002F Missing: check_ajax_referer( 'job_manager_nonce', 'nonce' );\n    \u002F\u002F Missing: if ( ! current_user_can( 'edit_post', $job_id ) ) return;\n\n    update_post_meta( $job_id, '_filled', 1 );\n    wp_send_json_success();\n}","--- a\u002Fincludes\u002Fclass-wp-job-manager-ajax.php\n+++ b\u002Fincludes\u002Fclass-wp-job-manager-ajax.php\n@@ -10,7 +10,6 @@\n- add_action( 'wp_ajax_nopriv_job_manager_mark_filled', array( $this, 'mark_job_filled' ) );\n- add_action( 'wp_ajax_nopriv_job_manager_mark_not_filled', array( $this, 'mark_job_not_filled' ) );\n+ add_action( 'wp_ajax_job_manager_mark_filled', array( $this, 'mark_job_filled' ) );\n+ add_action( 'wp_ajax_job_manager_mark_not_filled', array( $this, 'mark_job_not_filled' ) );\n \n public function mark_job_filled() {\n+    check_ajax_referer( 'job_manager_nonce', 'nonce' );\n     $job_id = absint( $_REQUEST['job_id'] );\n+    if ( ! current_user_can( 'edit_post', $job_id ) ) {\n+        wp_send_json_error( __( 'You do not have permission to edit this job.', 'wp-job-manager' ) );\n+    }\n     update_post_meta( $job_id, '_filled', 1 );\n     wp_send_json_success();\n }","1. Identify the ID of a target job listing.\n2. Obtain a valid AJAX nonce by visiting a public page where WP Job Manager scripts are enqueued (e.g., a page using the [jobs] shortcode). The nonce is typically found in the global JavaScript object 'job_manager_ajax_params'.\n3. Send an unauthenticated POST request to \u002Fwp-admin\u002Fadmin-ajax.php.\n4. Set the 'action' parameter to 'job_manager_mark_filled', the 'job_id' parameter to the target ID, and the '_wpnonce' parameter to the extracted nonce.\n5. The plugin will update the job's status or metadata (marking it as filled) without verifying that the requester is the owner of the job or an administrator.","gemini-3-flash-preview","2026-04-20 21:13:00","2026-04-20 21:13:20",{"type":32,"vulnerable_version":9,"fixed_version":9,"vulnerable_browse":9,"vulnerable_zip":9,"fixed_browse":9,"fixed_zip":9,"all_tags":33},"plugin","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-job-manager\u002Ftags"]