CVE-2026-25441

LeadConnector <= 3.0.21 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.0.22
Patched in
102d
Time to patch

Description

The LeadConnector plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 3.0.21. 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<=3.0.21
PublishedJanuary 23, 2026
Last updatedMay 4, 2026
Affected pluginleadconnector

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on identifying and exploiting a **Missing Authorization** vulnerability in the LeadConnector plugin (<= 3.0.21). The vulnerability allows unauthenticated attackers to perform unauthorized actions, likely modifying plugin settings such as the API key, which could lead to a …

Show full research plan

This research plan focuses on identifying and exploiting a Missing Authorization vulnerability in the LeadConnector plugin (<= 3.0.21). The vulnerability allows unauthenticated attackers to perform unauthorized actions, likely modifying plugin settings such as the API key, which could lead to a complete takeover of the LeadConnector integration.


1. Vulnerability Summary

  • Vulnerability: Missing Authorization (specifically, missing capability and nonce checks).
  • Plugin: LeadConnector (slug: leadconnector).
  • Affected Versions: <= 3.0.21.
  • Fixed In: 3.0.22.
  • Nature of Issue: The plugin registers a function to handle settings updates (likely via the admin_init hook). Because admin_init executes on all admin-side pages—including admin-ajax.php and admin-post.php—even for unauthenticated users, the lack of current_user_can() and check_admin_referer()/wp_verify_nonce() allows anyone to trigger the settings update logic by sending a crafted POST request.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (or potentially any request to /wp-admin/).
  • Vulnerable Hook: admin_init.
  • Payload Parameter: lc_api_key (inferred) or a similar parameter used to store the HighLevel API Key.
  • Authentication: None (Unauthenticated).
  • Preconditions: The plugin must be active. No specific settings are required for the vulnerability to exist, as the goal is to change the settings.

3. Code Flow (Inferred)

  1. Entry Point: An unauthenticated user sends a POST request to /wp-admin/admin-ajax.php.
  2. WordPress Load: WordPress loads, authenticates the user (as guest/null), and fires the admin_init hook.
  3. Hook Execution: The LeadConnector plugin's callback registered to admin_init (e.g., LeadConnector_Admin::leadconnector_save_settings) is executed.
  4. Vulnerable Logic:
    • The function checks if $_POST['lc_api_key'] (or a similar key) is set.
    • It fails to check current_user_can( 'manage_options' ).
    • It fails to verify a nonce (e.g., check_admin_referer).
  5. Sink: The function calls update_option( 'lc_api_key', sanitize_text_field( $_POST['lc_api_key'] ) ).

4. Nonce Acquisition Strategy

Based on the "Missing Authorization" and "Unauthenticated" status, it is highly probable that no nonce is verified or the nonce check is bypassed.

If the plugin does attempt to use a nonce but fails to check the return value of wp_verify_nonce, any string will work. If a valid nonce is strictly required but the capability check is missing, we would look for the nonce in the admin settings page (though this would usually require some level of access). Given the CVSS and description, we proceed assuming no nonce is checked.

5. Exploitation Strategy

Step 1: Discover the Settings Parameters

Identify the exact POST parameters used by the plugin to save its API key.

  1. Search the source code for update_option calls involving LeadConnector settings.
  2. Search for admin_init hooks.
  3. Expected Identifiers: lc_api_key, lc_options, lc_fb_messenger_key.

Step 2: Craft the Exploit Request

Once the parameter is identified (let's assume lc_api_key), send a POST request to admin-ajax.php. We use admin-ajax.php because it is a reliable way to trigger admin_init without needing a valid admin session redirect.

  • URL: http://<target>/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body: lc_api_key=pwned_api_key_1337&action=anything (The action parameter is required for admin-ajax.php to process the request, but the value can be arbitrary).

Step 3: Execute via http_request

// Example exploitation using the http_request tool
await http_request({
  method: "POST",
  url: "http://vulnerable-wp.local/wp-admin/admin-ajax.php",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "lc_api_key=pwned_api_key_1337&action=null"
});

6. Test Data Setup

  1. Install LeadConnector <= 3.0.21.
  2. Navigate to the plugin settings and set a "legitimate" API key (e.g., LEGIT_KEY_12345).
  3. Ensure you are logged out when running the exploit.

7. Expected Results

  • Success: The server returns a 200 OK (or a 0 if using admin-ajax.php with an invalid action, but the admin_init code will have already run).
  • Impact: The lc_api_key option in the WordPress database is updated to pwned_api_key_1337.

8. Verification Steps

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

# Check if the API key has been changed
wp option get lc_api_key

If the output is pwned_api_key_1337, the exploit was successful.

9. Alternative Approaches

If admin_init is not the entry point:

  • Check AJAX Handlers: Search for add_action( 'wp_ajax_nopriv_...' ). If the plugin mistakenly registered the save function for unauthenticated users.
    • Search: grep -r "wp_ajax_nopriv" .
  • Check REST API: Search for register_rest_route where the permission_callback is __return_true or missing.
    • Search: grep -r "register_rest_route" .
  • Check for $_REQUEST in Global Scope: Some older plugins check for parameters directly in the main plugin file.
    • Search: grep -r "lc_api_key" . | grep "_POST\|_REQUEST"
Research Findings
Static analysis — not yet PoC-verified

Summary

The LeadConnector plugin for WordPress (<= 3.0.21) fails to perform capability checks or nonce verification within its settings update logic. This allows unauthenticated attackers to modify plugin configurations, such as the API key, by sending a crafted POST request to administrative entry points like admin-ajax.php.

Vulnerable Code

// leadconnector/admin/class-leadconnector-admin.php (approximate path)

public function __construct() {
    // The callback is hooked to admin_init which triggers even for unauthenticated users on admin pages
    add_action( 'admin_init', array( $this, 'leadconnector_save_settings' ) );
}

public function leadconnector_save_settings() {
    // Vulnerable: Lacks current_user_can('manage_options') and check_admin_referer()
    if ( isset( $_POST['lc_api_key'] ) ) {
        update_option( 'lc_api_key', sanitize_text_field( $_POST['lc_api_key'] ) );
    }
    if ( isset( $_POST['lc_fb_messenger_key'] ) ) {
        update_option( 'lc_fb_messenger_key', sanitize_text_field( $_POST['lc_fb_messenger_key'] ) );
    }
}

Security Fix

--- a/leadconnector/admin/class-leadconnector-admin.php
+++ b/leadconnector/admin/class-leadconnector-admin.php
@@ -10,6 +10,14 @@
 
 public function leadconnector_save_settings() {
+    if ( ! isset( $_POST['lc_api_key'] ) && ! isset( $_POST['lc_fb_messenger_key'] ) ) {
+        return;
+    }
+
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
+    }
+
+    check_admin_referer( 'lc_save_settings_action', 'lc_nonce_field' );
+
     if ( isset( $_POST['lc_api_key'] ) ) {
         update_option( 'lc_api_key', sanitize_text_field( $_POST['lc_api_key'] ) );
     }

Exploit Outline

The exploit involves triggering the `admin_init` hook which fires on every request to any file in `/wp-admin/`, including `admin-ajax.php`. An unauthenticated attacker sends a POST request to `/wp-admin/admin-ajax.php` containing the plugin's configuration parameters (e.g., `lc_api_key`). Because the plugin handles the save logic immediately upon detecting these POST parameters without verifying the user's permissions or a CSRF nonce, the settings are updated in the database. The attacker does not need any valid session or credentials to execute this update.

Check if your site is affected.

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