LeadConnector <= 3.0.21 - Missing Authorization
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:NTechnical Details
<=3.0.21Source Code
WordPress.org SVNThis 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_inithook). Becauseadmin_initexecutes on all admin-side pages—includingadmin-ajax.phpandadmin-post.php—even for unauthenticated users, the lack ofcurrent_user_can()andcheck_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)
- Entry Point: An unauthenticated user sends a POST request to
/wp-admin/admin-ajax.php. - WordPress Load: WordPress loads, authenticates the user (as guest/null), and fires the
admin_inithook. - Hook Execution: The LeadConnector plugin's callback registered to
admin_init(e.g.,LeadConnector_Admin::leadconnector_save_settings) is executed. - 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).
- The function checks if
- 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.
- Search the source code for
update_optioncalls involving LeadConnector settings. - Search for
admin_inithooks. - 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(Theactionparameter is required foradmin-ajax.phpto 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
- Install LeadConnector <= 3.0.21.
- Navigate to the plugin settings and set a "legitimate" API key (e.g.,
LEGIT_KEY_12345). - 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.phpwith an invalid action, but theadmin_initcode will have already run). - Impact: The
lc_api_keyoption in the WordPress database is updated topwned_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" .
- Search:
- Check REST API: Search for
register_rest_routewhere thepermission_callbackis__return_trueor missing.- Search:
grep -r "register_rest_route" .
- Search:
- Check for
$_REQUESTin Global Scope: Some older plugins check for parameters directly in the main plugin file.- Search:
grep -r "lc_api_key" . | grep "_POST\|_REQUEST"
- Search:
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
@@ -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.