CVE-2026-25404

WP Job Manager <= 2.4.0 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.4.1
Patched in
96d
Time to patch

Description

The WP Job Manager plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.4.0. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.4.0
PublishedJanuary 29, 2026
Last updatedMay 4, 2026
Affected pluginwp-job-manager

What Changed in the Fix

Changes introduced in v2.4.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-25404 (WP Job Manager) ## 1. Vulnerability Summary The **WP Job Manager** plugin (up to version 2.4.0) is vulnerable to **Missing Authorization**. The vulnerability exists in the `Job_Dashboard_Shortcode::handle_actions` method (or similar action handlers wit…

Show full research plan

Exploitation Research Plan - CVE-2026-25404 (WP Job Manager)

1. Vulnerability Summary

The WP Job Manager plugin (up to version 2.4.0) is vulnerable to Missing Authorization. The vulnerability exists in the Job_Dashboard_Shortcode::handle_actions method (or similar action handlers within the job dashboard logic). While the plugin implements CSRF protection via nonces, it fails to perform adequate capability checks or ownership verification for specific actions such as duplicating a job listing or changing a job's status (e.g., marking as filled). This allows unauthenticated attackers to perform unauthorized state-changing actions on arbitrary job listings if they can obtain a valid nonce, which is typically exposed to all users visiting the dashboard page.

2. Attack Vector Analysis

  • Endpoint: The main site URL or the specific page containing the [job_dashboard] shortcode.
  • Hook: wp hook, registered in WP_Job_Manager\Job_Dashboard_Shortcode::__construct.
  • Query Parameters:
    • action: The action to perform (e.g., duplicate, mark_filled, mark_not_filled).
    • job_id: The ID of the target job listing.
    • _wpnonce: A WordPress nonce for the action job_manager_my_job_actions.
  • Authentication: Unauthenticated (PR:N).
  • Precondition: A job listing must exist, and its ID must be known. The attacker must visit the dashboard page (even if logged out) to retrieve the nonce.

3. Code Flow

  1. Entry Point: A GET request is sent to /?action=mark_filled&job_id=123&_wpnonce=[NONCE].
  2. Hook Execution: WP_Job_Manager\Job_Dashboard_Shortcode::handle_actions() is triggered via the wp hook.
  3. Action Parsing: The code extracts action and job_id from $_REQUEST.
  4. Nonce Verification: The code calls wp_verify_nonce( $_REQUEST['_wpnonce'], 'job_manager_my_job_actions' ). For unauthenticated users, this nonce is generated using uid=0.
  5. Vulnerable Sink: The code proceeds to execute the action (e.g., updating post meta to mark a job as filled or duplicating the post) without verifying that the current user is logged in or that the user has the authority to manage the specific job_id.
  6. State Change: The job listing's status or metadata is modified in the database.

4. Nonce Acquisition Strategy

The job_manager_my_job_actions nonce is generated for unauthenticated users when they visit the page containing the [job_dashboard] shortcode. Even though the dashboard content is hidden behind a login form for guests, the underlying logic (specifically Job_Overlay) often enqueues scripts and localizes data including the nonce.

Strategy:

  1. Identify Dashboard: Locate the page containing [job_dashboard].
  2. Navigate: Use browser_navigate to visit that page.
  3. Extract: Use browser_eval to extract the nonce from the localized JavaScript objects.
    • Target Variable: window.wp_job_manager_job_dashboard (inferred from typical WPJM localization patterns).
    • Nonce Key: nonce.
    • Command: browser_eval("window.wp_job_manager_job_dashboard?.nonce").

5. Exploitation Strategy

  1. Setup: Create a target job listing as an admin and record its ID.
  2. Preparation: Create a page with the [job_dashboard] shortcode.
  3. Extraction: Visit the dashboard page as a guest and extract the nonce.
  4. Execution: Use http_request to send a GET request to the site root with the target parameters.
    • URL: http://localhost:8080/
    • Method: GET
    • Params: ?action=mark_filled&job_id=[JOB_ID]&_wpnonce=[NONCE]
  5. Verification: Check if the job listing's metadata _filled has been set to 1.

6. Test Data Setup

  1. Target Job: Create a job listing.
    wp post create --post_type=job_listing --post_title="Critical Engineering Role" --post_status=publish --post_author=1
    # Record the ID (e.g., 123)
    
  2. Dashboard Page: Create a page to trigger nonce localization.
    wp post create --post_type=page --post_title="Employer Dashboard" --post_content='[job_dashboard]' --post_status=publish
    

7. Expected Results

  • The HTTP request returns a redirect (302) or success message.
  • The job listing with the specified ID is updated.
  • Specifically, for the mark_filled action, the post meta _filled will be changed from 0 (or non-existent) to 1.

8. Verification Steps

Verify the state change via WP-CLI:

# Check if the job is marked as filled
wp post generate --post_type=job_listing --post_id=[ID] --field=_filled
# Or check the display status via the dashboard action logic
wp eval "echo get_post_meta([ID], '_filled', true);"

9. Alternative Approaches

If mark_filled is properly protected, attempt the duplicate action:

  1. Action: duplicate.
  2. Request: GET /?action=duplicate&job_id=[ID]&_wpnonce=[NONCE].
  3. Verification: Check for the creation of a new job listing with a title like "Duplicate of [Original Title]".
    wp post list --post_type=job_listing --orderby=ID --order=DESC --limit=1
    

If the wp hook is not the entry point, investigate the job_manager_ajax_ handlers in includes/class-wp-job-manager-ajax.php, though the shortcode-based action handler is the most probable location for this specific authorization failure.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.