
سكربت فرز للقراءة فقط لمواقع WordPress بعد PHP2Shell (CVE-2026-109618، CVE-2026-109619). يتحقق من علامات الاختراق ويكتب تقريرًا لكل موقع.
سكربت فرز للقراءة فقط لمواقع WordPress بعد PHP2Shell (CVE-2026-109618 وCVE-2026-109619)، وهما ثغرتا تنفيذ تعليمات برمجية عن بُعد حرجتان في محرك PHP أُعلن عنهما في 21 أكتوبر 2026.
إصلاح PHP يغلق الباب، لكن مصائد Amazon (honeypots) سجّلت محاولة استغلال في 17 أكتوبر، أي قبل أربعة أيام من الإفصاح العلني، حين كانت الثغرتان لا تزالان تحت الحظر. هذا هو أبكر نشاط نشره أحد، وليس اليوم الذي بدأت فيه الهجمات، لذا تعامل معه كحدٍّ أدنى. أي موقع كان يمكن الوصول إليه عبر PHP غير مُرقَّع خلال تلك الفترة قد يكون فيه باب خلفي بالفعل، وتحديث PHP لن يزيله. يفحص هذا السكربت موقع WordPress واحدًا أو عدة مواقع بحثًا عن الأشياء التي يتركها عادةً مهاجم يملك تنفيذ التعليمات البرمجية، ويكتب تقرير Markdown لكل موقع.
أُعلن عن ثغرة ثالثة، CVE-2026-115560، في 23 أكتوبر. لا يوجد لها إصدار
مُصلَح بعد: الإصدارات النقطية لأكتوبر (8.2.35، 8.3.36، 8.4.27، 8.5.12)
لا تغطيها، والإصلاح مُقرَّر مع الإصدار الدوري التالي في نوفمبر. يطلب php.net
من الجميع ضبط session.upload_progress.enabled=0 في php.ini أو في
مجمّع PHP-FPM حتى ذلك الحين. يتحقق السكربت مما إذا كان ذلك قد تم.
لا يعدّل أو يحذف أي شيء أبدًا. إنه يسرد ويتحقق ويُبلّغ. وما تفعله بالنتائج يعود إليك.
| المجال | الفحص |
|---|---|
| PHP | إصدار CLI المثبَّت مقابل الإصدارات المُصلَحة (8.2.35، 8.3.36، 8.4.27، 8.5.12). يُشير إلى الفروع المنتهية العمر، التي لم يصدر لها php.net إصدارًا؛ انظر فروع PHP الأقدم. |
| تقدّم الرفع | ما إذا كان session.upload_progress.enabled=0، وهو تخفيف CVE-2026-115560، مضبوطًا حيث يهم: رؤية PHP-FPM الخاصة (php-fpm -i)، وكل php.ini قابل للقراءة، وconf.d، ومجمّع FPM، وإعداد Apache. يُبلَّغ عن .user.ini و.htaccess على مستوى الموقع لكن لا يُحتسبان، لأن php.net يقول إنهما لا يعملان لهذا الإعداد. |
| سلامة النواة | wp core verify-checksums مقابل wordpress.org |
| سلامة الإضافات | wp plugin verify-checksums --all، ويسرد الإضافات المدفوعة/المخصّصة التي لا يمكنه التحقق منها |
| الملفات المرفوعة | أي ملف قابل للتنفيذ بـ PHP تحت wp-content/uploads (يتجاهل ملفات "Silence is golden" البديلة) |
| التغييرات الأخيرة | ملفات PHP والإعدادات المعدّلة منذ بداية الفترة (الافتراضي 2026-10-17). تُحتسب حزم ترجمة WordPress (wp-content/languages/**/*.l10n.php) بشكل منفصل بمجرد اجتيازها اختبار شكل صارم؛ وأي حزمة تفشل فيه تُعلَّم. |
| خطافات Prepend | auto_prepend_file / auto_append_file في .user.ini و.htaccess وphp.ini |
| الإضافات Must-use | محتويات wp-content/mu-plugins (تُنفَّذ في كل طلب، ومخفية عن قائمة الإضافات العادية) |
| أنماط التعليمات البرمجية | توقيعات web-shell الكلاسيكية (eval(base64_decode(...))، $_POST[...]()، إلخ) في wp-content |
| المستخدمون | جميع المسؤولين، وأي حساب سُجِّل داخل الفترة |
| قاعدة البيانات | siteurl/home، والخيارات والمقالات التي تحتوي على سكربتات مُحقَنة أو تعليمات برمجية مُموَّهة |
| Cron | أحداث WP-Cron المجدولة |
| السجلات | سجلات الوصول: استجابات 5xx يوميًا للفترة مقابل خط أساس يمتد حتى 7 أيام قبلها، إضافةً إلى عناوين URL التي أعادت 5xx خلال الفترة. سجلات الأخطاء وPHP-FPM: انهيارات العمّال (segfault/bus error). |
| الخادم | crontab الخاص بمستخدم الموقع وauthorized_keys في SSH |
| Docker | كل حاوية قيد التشغيل تحتوي على ملف php تنفيذي: إصدار PHP بداخلها، وما إذا كانت الحاوية لا تزال تشغّل إصدارًا أقدم من الصورة على القرص (مسحوبة لكن لم يُعَد إنشاؤها) |
يُشغَّل WP-CLI مع --skip-plugins --skip-themes حتى لا تتمكن إضافة أو قالب
مخترق من التنفيذ أثناء تشغيل الفحص.
على كل جهاز يُفحَص:
bash 3.2+، وfind، وgrep، وsort -V (قياسية على مضيفات Linux؛ وتعمل أيضًا من محطة عمل macOS قياسية)php على المسار، لفحص الإصدارللمواقع البعيدة: وصول بمفتاح SSH من الجهاز الذي تشغّل عليه السكربت. يشغّل السكربت نفسه عبر SSH؛ ولا يحتاج تثبيت أي شيء على المضيف البعيد عدا ما سبق.
curl -O https://raw.githubusercontent.com/brentmartinmiller/php2shell-check/main/php2shell-check.sh
chmod +x php2shell-check.sh
# One site
./php2shell-check.sh /var/www/example.com
less php2shell-reports/*.md
# Every site on the box
printf '%s\n' /var/www/*/public_html > sites.txt
./php2shell-check.sh sites.txt
cat php2shell-reports/SUMMARY.md
شغّله بصفتك المستخدم الذي يملك ملفات الموقع حيثما أمكن. فهو يعمل بصفة root
(يحصل WP-CLI على --allow-root)، لكن فحوصات crontab ومفتاح SSH عندئذٍ تفحص
بيانات root بدلاً من بيانات مستخدم الويب.
git clone https://github.com/brentmartinmiller/php2shell-check.git
cd php2shell-check
cp sites.txt.example sites.txt # edit: one site per line
./php2shell-check.sh sites.txt
لكل إدخال user@host:path، يتصل السكربت عبر SSH، ويشغّل نفسه هناك، ويعيد التقرير إلى php2shell-reports/ على جهازك. لا يتم تثبيت أي شيء على المضيف البعيد. يعمل SSH مع BatchMode=yes، لذا يلزم الوصول القائم على المفاتيح الذي يعمل دون طلب كلمة مرور.
تقدّم معظم المضيفات المشتركة (Hostinger وSiteGround وBluehost وما شابهها) خدمة SSH، لكنها غالبًا لا تتضمن wp في المسار. تحقق باستخدام which wp. إذا لم يكن موجودًا، نزّل WP-CLI مرة واحدة إلى دليلك الرئيسي ووجّه السكربت إليه:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
WP="php $HOME/wp-cli.phar" ./php2shell-check.sh domains/example.com/public_html
بدون WP-CLI، لا تزال فحوصات نظام الملفات تعمل، ويشير التقرير إلى ما تم تخطيه. في كلتا الحالتين، اسأل المضيف مباشرةً عن موعد طرحه لإصدار PHP المُرقَّع، لأن ذلك هو الشيء الوحيد الذي لا يمكن لفحص على مستوى الحساب رؤيته.
يحتوي sites.txt على موقع واحد في كل سطر، إما مسار محلي أو user@host:path.
المسارات البعيدة النسبية نسبية إلى الدليل الرئيسي لمستخدم SSH. يتم تجاهل الأسطر
التي تبدأ بـ #. راجع sites.txt.example. الملف موجود في
.gitignore حتى لا تنتهي أسماء مضيفي العملاء في أحد الـ commits.
تنتمي منافذ SSH غير القياسية والمفاتيح ومضيفي القفز إلى ~/.ssh/config:
Host client-a
HostName 203.0.113.10
Port 2222
User deploy
IdentityFile ~/.ssh/client-a
ثم أدرج الموقع كـ client-a:/var/www/html.
لا يخبرك php -v على مضيف Docker بأي شيء عن الحاويات، وسحب صورة ثابتة لا يُصلح شيئًا حتى تُعاد إنشاء الحاوية منها. ظل موقع أحد الفرق على الإصدار 8.3.35 لمدة ثلاثة أيام بعد السحب لهذا السبب تحديدًا.
عندما يعمل السكربت على مضيف يعمل عليه docker ps، يحصل التقرير على قسم Docker containers يسرد كل حاوية قيد التشغيل تحتوي على ملف تنفيذي php، وإصدار PHP داخلها، وما إذا كانت الحاوية قديمة (توجد نسخة بناء أحدث من صورتها على القرص لكن الحاوية لم تُعد إنشاؤها قط). أعد الإنشاء باستخدام docker compose up -d، أو أوقف الحاوية وأزلها ثم شغّلها مرة أخرى.
لتشغيل الفحص الكامل داخل حاوية، أدرجها كهدف:
docker:wordpress-php-1:/var/www/html
يتم تمرير السكربت عبر الأنبوب إلى docker exec ويعمل هناك؛ لا شيء يُثبَّت
داخل الحاوية. يحتاج إلى bash داخل الحاوية. صور php:* وwordpress:* الرسمية
المبنية على Debian تتضمنه؛ أما المبنية على Alpine (-alpine) فلا تتضمنه، لذا إما أن تنفّذ
docker exec NAME apk add bash أولًا أو تكتفي بـ docker exec NAME php -v. عادةً ما تكون قاعدة البيانات في
حاوية منفصلة، لذا تُتخطى فحوصات المستخدم والخيارات وcron ما لم تتمكن حاوية PHP
من الوصول إليها.
عيّن هذه كمتغيرات بيئة:
| المتغير | الافتراضي | المعنى |
|---|---|---|
SINCE | 2026-10-17 | بداية النافذة الزمنية لفحوصات "المعدَّل مؤخرًا" و"المسجَّل مؤخرًا". هذا أول محاولة استغلال مؤكدة، وليس أول هجوم؛ عيّنه أبكر للمواقع المهمة |
WP | wp | أمر WP-CLI على الهدف، مثل WP="php $HOME/wp-cli.phar" للمضيفين المشتركين |
LOGS | تلقائي | مسارات أو أنماط glob مفصولة بمسافات لسجلات الوصول والأخطاء وPHP-FPM المراد فحصها، مثل LOGS="/var/log/nginx/*.log* /var/log/php8.3-fpm.log*". افتراضيًا يبحث السكربت بجوار الموقع (../logs)، وفي ~/logs و~/access-logs و~/domains/*/logs، ثم تحت /var/log. تُقرأ أيضًا ملفات .gz المدوَّرة |
OUT | ./php2shell-reports | المكان الذي تُكتب فيه التقارير |