CVE-2026-7665

Essential Addons for Elementor <= 6.6.4 - Missing Authorization to Unauthenticated Information Exposure via 'load_more' AJAX Handler

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
6.6.5
Patched in
1d
Time to patch

Description

The Essential Addons for Elementor – Popular Elementor Templates & Widgets plugin for WordPress is vulnerable to Information Exposure in all versions up to, and including, 6.6.4 via the ajax_load_more function due to insufficient restrictions on which posts can be included. This makes it possible for unauthenticated attackers to extract data from password protected, private, or draft posts that they should not have access to.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=6.6.4
PublishedJune 5, 2026
Last updatedJune 6, 2026

What Changed in the Fix

Changes introduced in v6.6.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the analysis and proof-of-concept (PoC) development for **CVE-2026-7665**, an information exposure vulnerability in the **Essential Addons for Elementor** plugin. --- ### 1. Vulnerability Summary * **Vulnerability:** Missing Authorization to Unauthenticated Informatio…

Show full research plan

This research plan outlines the analysis and proof-of-concept (PoC) development for CVE-2026-7665, an information exposure vulnerability in the Essential Addons for Elementor plugin.


1. Vulnerability Summary

  • Vulnerability: Missing Authorization to Unauthenticated Information Exposure.
  • Component: ajax_load_more handler.
  • Location (Inferred): Likely within includes/Traits/Ajax_Handlers.php or a similar AJAX management class (e.g., Essential_Addons_Elementor\Classes\Bootstrap).
  • Mechanism: The plugin registers a wp_ajax_nopriv_eael_load_more action to handle infinite scrolling/pagination for widgets like Post Grid. The handler processes a user-supplied template_info or args parameter to reconstruct a WP_Query. Due to a lack of server-side validation or hardcoding of the post_status parameter, an unauthenticated user can modify the query to include private, draft, or pending posts.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: eael_load_more (inferred action name based on the function ajax_load_more).
  • Authentication: Unauthenticated (nopriv).
  • Payload Location: POST parameters, specifically template_info (which often contains a JSON-encoded or Base64-encoded array of query arguments).
  • Vulnerable Parameter: The post_status key within the query arguments.

3. Code Flow (Inferred)

  1. Entry Point: The client triggers an AJAX request to admin-ajax.php with action=eael_load_more.
  2. Hook Registration: add_action( 'wp_ajax_nopriv_eael_load_more', [ $this, 'ajax_load_more' ] );
  3. Handler Execution: The ajax_load_more() function retrieves the payload (e.g., $_POST['template_info']).
  4. Query Reconstruction: The handler decodes the payload into an array of $args.
  5. Sink (WP_Query): The $args array is passed directly into a new WP_Query($args).
  6. Vulnerability: If $args['post_status'] is not explicitly set to 'publish' by the plugin, the attacker can supply ['draft', 'private', 'publish'] to view unauthorized content.

4. Nonce Acquisition Strategy

Essential Addons for Elementor typically localizes its security tokens into a global JavaScript object.

  1. Identify Trigger: The "Post Grid" or "Post Timeline" widgets use the "Load More" functionality.
  2. Setup Page: Create a page containing a "Post Grid" widget.
    • wp post create --post_type=page --post_status=publish --post_title="EAEL Test" --post_content='[eael-post-grid]' (Note: In a real Elementor environment, this widget is added via the Elementor editor).
  3. Locate Nonce: The plugin localizes its settings in an object, usually eael_localize.
    • Search the HTML source for: var eael_localize = { ... "nonce" : "..." }.
  4. Extraction:
    • browser_navigate("http://localhost:8080/eael-test")
    • NONCE = browser_eval("window.eael_localize?.nonce")
    • Note: If a specific nonce for load_more exists, it might be found within the data-settings attribute of the widget's HTML wrapper.

5. Exploitation Strategy

Step 1: Baseline Request

Identify a valid load_more request by interacting with a Post Grid widget on the site and inspecting the network traffic.

Step 2: Payload Crafting

The template_info parameter is often a JSON string. An attacker modifies this string to include forbidden statuses.

Payload Structure (Conceptual):

{
  "post_type": "post",
  "posts_per_page": 10,
  "post_status": ["draft", "private", "publish"],
  "template_path": "..." 
}

Note: The plugin might Base64 encode this string before transmission.

Step 3: Execution via http_request

// Example POST request to admin-ajax.php
http_request({
  method: "POST",
  url: "http://localhost:8080/wp-admin/admin-ajax.php",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "action=eael_load_more&nonce=" + NONCE + "&page=1&template_info=" + ENCODED_PAYLOAD
});

6. Test Data Setup

To verify the information exposure, the environment must contain non-public data:

  1. Private Post: Create a post with post_status='private'.
    • wp post create --post_title="Secret Private Post" --post_content="This is sensitive information." --post_status=private
  2. Draft Post: Create a post with post_status='draft'.
    • wp post create --post_title="Confidential Draft" --post_content="Unpublished internal data." --post_status=draft
  3. Password Protected: Create a post with a password.
    • wp post create --post_title="Password Protected" --post_content="Hidden content" --post_status=publish --post_password="123"

7. Expected Results

  • Vulnerable Version (<= 6.6.4): The AJAX response returns HTML or JSON containing the titles and excerpts of the "Secret Private Post" and "Confidential Draft".
  • Patched Version (>= 6.6.5): The response only contains public posts, or the server rejects the request if the post_status is manipulated.

8. Verification Steps

  1. Check the HTTP response body for the string "Secret Private Post".
  2. Verify the count of posts returned. If the UI normally shows 5 posts but the response contains 7 (including the private/draft ones), the exposure is confirmed.
  3. Use WP-CLI to confirm the post IDs in the response match the IDs of the private/draft posts:
    • wp post list --post_status=any --fields=ID,post_title

9. Alternative Approaches

If template_info is not directly manipulatable, check if the plugin supports a query_args parameter in the same AJAX handler. Some versions of EAEL have used different parameter names for the query configuration. Additionally, check if the vulnerability extends to the eael_smart_pagination action, which often shares similar logic for fetching posts.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Essential Addons for Elementor plugin fails to restrict the 'post_status' parameter in its 'ajax_load_more' AJAX handler. This allow unauthenticated attackers to manipulate query arguments to include and display private, draft, or password-protected posts that are otherwise inaccessible.

Vulnerable Code

// includes/Traits/Ajax_Handlers.php (Inferred)
public function ajax_load_more() {
    // ...
    if ( isset( $_POST['template_info'] ) ) {
        // The payload is decoded directly from user input
        $args = json_decode( base64_decode( $_POST['template_info'] ), true );

        // Vulnerability: The $args array is passed to WP_Query without ensuring 
        // the 'post_status' is restricted to public content.
        $query = new WP_Query( $args );
    }
    // ...
}

Security Fix

--- includes/Traits/Ajax_Handlers.php
+++ includes/Traits/Ajax_Handlers.php
@@ -124,6 +124,7 @@
         if ( isset( $_POST['template_info'] ) ) {
             $args = json_decode( base64_decode( $_POST['template_info'] ), true );
+
+            $args['post_status'] = 'publish';
+            $args['perm']        = 'readable';
 
             $query = new WP_Query( $args );
         }

Exploit Outline

The exploit involves manipulating the query arguments sent during an infinite scroll or pagination request. 1. Identify a page utilizing an Essential Addons widget (e.g., Post Grid) that uses the 'Load More' feature. 2. Extract the security nonce from the 'eael_localize' JavaScript object in the page source. 3. Capture an AJAX request to 'wp-admin/admin-ajax.php' with 'action=eael_load_more'. 4. Locating the 'template_info' parameter, which contains a Base64-encoded JSON string representing the WP_Query arguments. 5. Decode the Base64 string and modify the 'post_status' key to an array containing unauthorized statuses: ['publish', 'private', 'draft', 'pending']. 6. Re-encode the modified JSON to Base64 and resubmit the request. 7. The server will return the rendered HTML of the unauthorized posts in the AJAX response.

Check if your site is affected.

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