
WordPress साइटों के लिए PHP2Shell (CVE-2026-109618, CVE-2026-109619) के बाद का रीड-ओनली ट्राइएज स्क्रिप्ट। यह साइट के समझौते के संकेतों की जाँच करता है और प्रति साइट एक रिपोर्ट लिखता है।
PHP2Shell (CVE-2026-109618 और CVE-2026-109619) के बाद WordPress साइटों के लिए एक read-only triage स्क्रिप्ट, जो PHP इंजन में मौजूद दो गंभीर remote code execution बग हैं और जिनका खुलासा 21 अक्टूबर, 2026 को हुआ था।
PHP को पैच करना दरवाज़ा बंद कर देता है, लेकिन Amazon के honeypots ने 17 अक्टूबर को एक exploitation प्रयास दर्ज किया, सार्वजनिक खुलासे से चार दिन पहले, जब बग अभी भी embargo में थे। यह वह सबसे पहली गतिविधि है जो किसी ने प्रकाशित की है, न कि वह दिन जब हमले शुरू हुए, इसलिए इसे एक न्यूनतम सीमा (floor) मानें। जो भी साइट उस अवधि के दौरान किसी अपैच किए गए PHP पर पहुँच योग्य थी, उसमें पहले से ही कोई backdoor हो सकता है, और PHP को अपडेट करने से वह हटेगा नहीं। यह स्क्रिप्ट एक या कई WordPress साइटों में उन चीज़ों की जाँच करती है जो code execution वाला हमलावर आमतौर पर पीछे छोड़ देता है, और प्रत्येक के लिए एक Markdown रिपोर्ट लिखती है।
एक तीसरा बग, CVE-2026-115560, 23 अक्टूबर को घोषित किया गया था। इसका अभी तक कोई fixed build नहीं है: अक्टूबर के point releases (8.2.35, 8.3.36, 8.4.27, 8.5.12) इसे कवर नहीं करते, और इसका फिक्स नवंबर में अगले नियमित रिलीज़ के साथ आना है। php.net तब तक सभी से php.ini या PHP-FPM pool में session.upload_progress.enabled=0 सेट करने का अनुरोध करता है। स्क्रिप्ट जाँचती है कि ऐसा किया गया है या नहीं।
यह कभी कुछ भी संशोधित या हटाती नहीं है। यह सूचीबद्ध करती है, सत्यापित करती है और रिपोर्ट करती है। निष्कर्षों के साथ क्या करना है, यह आप पर निर्भर है।
| क्षेत्र | जाँच |
|---|---|
| PHP | इंस्टॉल किया गया CLI संस्करण बनाम fixed releases (8.2.35, 8.3.36, 8.4.27, 8.5.12)। end-of-life शाखाओं को चिह्नित करती है, जिन्हें php.net से कोई रिलीज़ नहीं मिला; देखें पुरानी PHP शाखाएँ। |
| Upload progress | क्या session.upload_progress.enabled=0, जो CVE-2026-115560 का उपाय है, वहाँ सेट है जहाँ यह मायने रखता है: PHP-FPM का अपना दृश्य (php-fpm -i), प्रत्येक पठनीय php.ini, conf.d, FPM pool और Apache config। साइट-स्तरीय .user.ini और .htaccess की रिपोर्ट तो की जाती है लेकिन उन्हें श्रेय नहीं दिया जाता, क्योंकि php.net कहता है कि ये इस सेटिंग के लिए काम नहीं करते। |
| Core integrity | wordpress.org के विरुद्ध wp core verify-checksums |
| Plugin integrity | wp plugin verify-checksums --all, और उन premium/custom plugins की सूची जिन्हें सत्यापित नहीं किया जा सकता |
| Uploads | wp-content/uploads के अंतर्गत कोई भी PHP-executable फ़ाइल ("Silence is golden" stubs को अनदेखा करती है) |
| Recent changes | विंडो प्रारंभ (डिफ़ॉल्ट 2026-10-17) के बाद संशोधित PHP और config फ़ाइलें। WordPress translation packs (wp-content/languages/**/*.l10n.php) को एक कड़े shape test से गुज़रने पर अलग से गिना जाता है; जो इसमें विफल होता है उसे चिह्नित किया जाता है। |
| Prepend hooks | .user.ini, .htaccess, php.ini में auto_prepend_file / auto_append_file |
| Must-use plugins | wp-content/mu-plugins की सामग्री (हर अनुरोध पर चलती है, सामान्य plugin सूची से छिपी रहती है) |
| Code patterns | wp-content में क्लासिक web-shell हस्ताक्षर (eval(base64_decode(...)), $_POST[...](), आदि) |
| Users | सभी administrators, और विंडो के भीतर पंजीकृत कोई भी खाता |
| Database | siteurl/home, इंजेक्ट किए गए स्क्रिप्ट या obfuscated कोड वाले options और posts |
| Cron | WP-Cron निर्धारित इवेंट |
| Logs | Access logs: विंडो के लिए प्रति दिन 5xx प्रतिक्रियाएँ बनाम इससे पहले के 7 दिनों तक के baseline के विरुद्ध, साथ ही विंडो में 5xx लौटाने वाले URLs। Error और PHP-FPM logs: worker crashes (segfault/bus error)। |
| Server | साइट उपयोगकर्ता का crontab और SSH authorized_keys |
| Docker | php binary वाला प्रत्येक चल रहा container: उसके अंदर का PHP संस्करण, और क्या container अभी भी डिस्क पर मौजूद image से पुराना build चला रहा है (pulled लेकिन recreated नहीं) |
WP-CLI को --skip-plugins --skip-themes के साथ चलाया जाता है ताकि जाँच के दौरान कोई backdoored plugin या theme निष्पादित न हो सके।
प्रत्येक जाँची जाने वाली मशीन पर:
bash 3.2+, find, grep और sort -V (Linux hosts पर मानक; एक स्टॉक macOS workstation से भी काम करता है)phpरिमोट साइटों के लिए: उस मशीन से SSH key पहुँच जिस पर आप स्क्रिप्ट चलाते हैं। स्क्रिप्ट SSH के माध्यम से स्वयं चलती है; रिमोट host पर उपरोक्त के अतिरिक्त कुछ भी इंस्टॉल करने की आवश्यकता नहीं है।
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-key जाँच वेब उपयोगकर्ता के बजाय
root के crontab और SSH-key की जाँच करते हैं।
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 के साथ चलता है, इसलिए ऐसी key-आधारित
पहुँच आवश्यक है जो बिना पासवर्ड प्रॉम्प्ट के काम करे।
अधिकांश साझा होस्ट (Hostinger, SiteGround, Bluehost और इसी तरह के अन्य) SSH प्रदान करते हैं लेकिन
अक्सर उनके path में 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 में है ताकि क्लाइंट होस्टनाम किसी कमिट में न आ जाएँ।
गैर-मानक SSH पोर्ट, कुंजियाँ और जंप होस्ट ~/.ssh/config में होने चाहिए:
Host client-a
HostName 203.0.113.10
Port 2222
User deploy
IdentityFile ~/.ssh/client-a
फिर साइट को client-a:/var/www/html के रूप में सूचीबद्ध करें।
Docker होस्ट पर php -v कंटेनरों के बारे में कुछ नहीं बताता, और एक निश्चित इमेज खींचने से तब तक कुछ भी पैच नहीं होता जब तक कंटेनर को उससे फिर से न बनाया जाए। एक टीम की साइट ठीक इसी कारण से खींचने के बाद तीन दिनों तक 8.3.35 पर बनी रही।
जब स्क्रिप्ट ऐसे होस्ट पर चलती है जहाँ docker ps काम करता है, तो रिपोर्ट में एक Docker containers अनुभाग जुड़ता है जिसमें हर चल रहे कंटेनर की सूची होती है जिसमें php बाइनरी है, उसके अंदर का PHP संस्करण, और क्या कंटेनर पुराना (stale) है (उसकी इमेज का नया बिल्ड डिस्क पर है लेकिन कंटेनर कभी फिर से नहीं बनाया गया)। docker compose up -d से फिर से बनाएँ, या कंटेनर को रोकें, हटाएँ और फिर से चलाएँ।
कंटेनर के अंदर पूरा चेक चलाने के लिए, उसे लक्ष्य के रूप में सूचीबद्ध करें:
docker:wordpress-php-1:/var/www/html