CVE-2026-2515

Hostinger Reach <= 1.3.8 - Missing Authorization to Authenticated (Subscriber+) Integration API Key Update

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.3.9
Patched in
10d
Time to patch

Description

The Hostinger Reach – AI-Powered Email Marketing for WordPress plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the 'handle_ajax_action' function in all versions up to, and including, 1.3.8. This makes it possible for authenticated attackers, with Subscriber-level access and above, to use the 'hostinger_reach_connection_notice_action' action to update the API key value stored in the database. This vulnerability can only be exploited when the plugin is not connected to a site and no API key value exists in the database.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.3.8
PublishedMay 12, 2026
Last updatedMay 21, 2026
Affected pluginhostinger-reach

What Changed in the Fix

Changes introduced in v1.3.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-2515 - Hostinger Reach API Key Unauthorized Update ## 1. Vulnerability Summary The **Hostinger Reach** plugin for WordPress (versions <= 1.3.8) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, the function `handle_ajax_action` (assoc…

Show full research plan

Research Plan: CVE-2026-2515 - Hostinger Reach API Key Unauthorized Update

1. Vulnerability Summary

The Hostinger Reach plugin for WordPress (versions <= 1.3.8) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, the function handle_ajax_action (associated with the hostinger_reach_connection_notice_action action) fails to perform a capability check (e.g., current_user_can('manage_options')) before updating the plugin's API key in the database.

While the action is protected by a nonce, the nonce is generated and exposed to any authenticated user (including Subscribers) who can access the WordPress admin dashboard where the connection notice is displayed. An attacker can exploit this to inject a malicious API key, potentially hijacking the plugin's integration with Hostinger's email marketing services.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: hostinger_reach_connection_notice_action
  • Method: POST
  • Parameters:
    • action: hostinger_reach_connection_notice_action
    • nonce: A valid nonce for the action, found in the hostinger_reach_connection_notice_data JS object.
    • choice: The payload intended to be saved as the API key (or used to trigger the update).
  • Authentication: Authenticated (Subscriber-level access or higher).
  • Precondition: The plugin must not already be connected to a site, and the API key option in the database must be empty.

3. Code Flow

  1. Notice Enqueueing: The plugin enqueues frontend/dist/connection-notice.js and localizes it with hostinger_reach_connection_notice_data.
  2. Nonce Exposure: The localization includes a nonce generated via wp_create_nonce( 'hostinger_reach_connection_notice_action' ).
  3. AJAX Trigger: The JS in connection-notice.js listens for clicks on buttons with the class .hostinger-reach-action-button.
  4. Request Formation: When clicked, it sends a POST request to admin-ajax.php with the choice parameter taken from the button's data-action attribute.
  5. Vulnerable Sink: The PHP function handle_ajax_action receives the request:
    • It calls check_ajax_referer() or wp_verify_nonce() using the provided nonce.
    • Crucially, it skips current_user_can() checks.
    • It takes the value of $_POST['choice'] and updates a WordPress option (likely hostinger_reach_api_key or hostinger_reach_access_token) directly.

4. Nonce Acquisition Strategy

The nonce is localized for the connection-notice.js script. Even Subscribers see the admin notice if it is not restricted to administrators.

  1. Navigate: Login as a Subscriber and navigate to the main dashboard (/wp-admin/index.php).
  2. Extract: Use browser_eval to extract the nonce from the global JavaScript object:
    window.hostinger_reach_connection_notice_data?.nonce
    
  3. Verify Action: Ensure the action matches the one in the object:
    window.hostinger_reach_connection_notice_data?.action // Should be "hostinger_reach_connection_notice_action"
    

5. Exploitation Strategy

  1. Setup:
    • Install Hostinger Reach <= 1.3.8.
    • Ensure the plugin is not connected (check that the option hostinger_reach_api_key does not exist or is empty).
    • Create a Subscriber user.
  2. Capture Nonce:
    • Log in as the Subscriber.
    • Access /wp-admin/.
    • Run browser_eval to get the nonce from hostinger_reach_connection_notice_data.nonce.
  3. Trigger Exploit:
    • Use the http_request tool to send a POST request.
    • Payload:
      • URL: http://localhost:8888/wp-admin/admin-ajax.php
      • Headers: Content-Type: application/x-www-form-urlencoded
      • Body:
        action=hostinger_reach_connection_notice_action&nonce=[NONCE]&choice=pwned_api_key_1337
        
  4. Verification:
    • Use WP-CLI to check if the API key was updated in the database.

6. Test Data Setup

  • Plugin Version: 1.3.8
  • User: Subscriber (user_login: subscriber, user_pass: subscriber)
  • Initial State:
    wp option delete hostinger_reach_api_key --quiet
    wp option delete hostinger_reach_connection_data --quiet
    

7. Expected Results

  • The AJAX request should return a JSON success message: {"success": true, "data": { ... }}.
  • The choice parameter value should be saved into the database.
  • The response might include a redirect_url as seen in connection-notice.js.

8. Verification Steps

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

# Check common option names for API keys in this plugin
wp option get hostinger_reach_api_key
# OR
wp option get hostinger_reach_connection_data

If the output is pwned_api_key_1337, the exploit is successful.

9. Alternative Approaches

If the choice parameter is not the direct sink for the API key, it might be that the choice triggers a "Connect" process where the plugin expects a token or key parameter in the same request that isn't used by the JS but is processed by the PHP:

action=hostinger_reach_connection_notice_action&nonce=[NONCE]&choice=connect&api_key=pwned_api_key_1337

Check the PHP handle_ajax_action method for other $_POST or $_REQUEST parameters if choice alone does not work. Given the JS code t.append("choice", e), choice is the primary candidate.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Hostinger Reach plugin for WordPress (<= 1.3.8) fails to perform an authorization check in its AJAX handler for connection notices. This allows authenticated attackers, including those with low-level Subscriber permissions, to overwrite the plugin's API key by exploiting a nonce exposed in the admin dashboard.

Vulnerable Code

// frontend/dist/connection-notice.js
const e=e=>{
    const t=new FormData;
    t.append("action",hostinger_reach_connection_notice_data.action),
    t.append("nonce",hostinger_reach_connection_notice_data.nonce),
    t.append("choice",e),
    fetch(hostinger_reach_connection_notice_data.ajaxurl,{method:"POST",body:t})
    .then(n=>n.json())
    .then(e=>{
        e.success&&e.data.redirect_url&&(window.location.href=e.data.redirect_url);
        const t=n().querySelector(".notice-dismiss");
        t&&t.click(),notice.style.display="none"
    })
};

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/hostinger-reach/1.3.8/changelog.txt /home/deploy/wp-safety.org/data/plugin-versions/hostinger-reach/1.3.9/changelog.txt
--- /home/deploy/wp-safety.org/data/plugin-versions/hostinger-reach/1.3.8/changelog.txt	2026-02-26 12:19:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/hostinger-reach/1.3.9/changelog.txt	2026-03-03 11:49:14.000000000 +0000
@@ -204,3 +204,13 @@
 - Feature: Dependency update
 - Fix: Send CF7 name field
 - Dev: Improved automation
+
+1.3.9 (2026-03-03)
+
+- Feature: Add main menu entry in Reach
+- Fix: Limit Reach to admin users only
+- Fix: Expose missed CORS headers
+- Tweak: Update translations
+- Tweak: Update Readme
+- Tweak: Add Add Form Banner
+- Tweak: Improve UX for Connection Banner

Exploit Outline

The exploit targets the 'hostinger_reach_connection_notice_action' AJAX action. An attacker with Subscriber-level access logs into the WordPress dashboard and extracts a valid nonce from the global JavaScript object 'hostinger_reach_connection_notice_data'. The attacker then sends a POST request to '/wp-admin/admin-ajax.php' containing the 'action', the stolen 'nonce', and a 'choice' parameter. The 'choice' value is treated as the new API key by the server-side handler which lacks a 'current_user_can()' check, allowing the attacker to hijack the plugin's integration service. This exploitation is effective when the plugin is in an unconnected state.

Check if your site is affected.

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