
Script di triage in sola lettura per siti WordPress dopo PHP2Shell (CVE-2026-109618, CVE-2026-109619). Verifica i segni di compromissione e scrive un report per ogni sito.
Uno script di triage in sola lettura per siti WordPress dopo PHP2Shell (CVE-2026-109618 e CVE-2026-109619), la coppia di bug critici di esecuzione remota di codice nel motore PHP divulgati il 21 ottobre 2026.
Applicare la patch a PHP chiude la porta, ma gli honeypot di Amazon hanno registrato un tentativo di sfruttamento il 17 ottobre, quattro giorni prima della divulgazione pubblica, mentre i bug erano ancora sotto embargo. Quella è l'attività più remota che chiunque abbia pubblicato, non il giorno in cui sono iniziati gli attacchi, quindi va considerata come un limite inferiore. Qualsiasi sito raggiungibile con un PHP non aggiornato durante quella finestra potrebbe già avere una backdoor, e aggiornare PHP non la rimuoverà. Questo script controlla uno o più siti WordPress alla ricerca delle tracce che tipicamente lascia un attaccante con esecuzione di codice, e scrive un report Markdown per ciascuno.
Un terzo bug, CVE-2026-115560, è stato annunciato il 23 ottobre. Non ha
ancora una build corretta: le point release di ottobre (8.2.35, 8.3.36, 8.4.27,
8.5.12) non lo coprono, e la correzione è prevista con la prossima release
regolare di novembre. php.net chiede a tutti di impostare
session.upload_progress.enabled=0 in php.ini o nel pool PHP-FPM fino ad
allora. Lo script verifica se ciò è stato fatto.
Non modifica né elimina mai nulla. Elenca, verifica e riporta. Cosa fare con i risultati spetta a te.
| Area | Controllo |
|---|---|
| PHP | Versione CLI installata rispetto alle release corrette (8.2.35, 8.3.36, 8.4.27, 8.5.12). Segnala i rami a fine vita, che non hanno ricevuto alcuna release da php.net; vedi Rami PHP più vecchi. |
| Upload progress | Se session.upload_progress.enabled=0, la mitigazione per CVE-2026-115560, è impostata dove conta: la vista propria di PHP-FPM (php-fpm -i), ogni php.ini leggibile, conf.d, i pool FPM e la configurazione Apache. I file .user.ini e .htaccess a livello di sito vengono riportati ma non considerati validi, perché php.net afferma che non funzionano per questa impostazione. |
| Integrità del core | wp core verify-checksums rispetto a wordpress.org |
| Integrità dei plugin | wp plugin verify-checksums --all, ed elenca i plugin premium/personalizzati che non può verificare |
| Uploads | Qualsiasi file eseguibile da PHP sotto wp-content/uploads (ignora gli stub "Silence is golden") |
| Modifiche recenti | File PHP e di configurazione modificati dall'inizio della finestra (predefinito 2026-10-17). I pacchetti di traduzione di WordPress (wp-content/languages/**/*.l10n.php) vengono conteggiati separatamente una volta superato un rigoroso test di forma; uno che non lo supera viene segnalato. |
| Hook prepend | auto_prepend_file / auto_append_file in .user.ini, .htaccess, php.ini |
| Plugin must-use | Contenuto di wp-content/mu-plugins (viene eseguito a ogni richiesta, nascosto dalla normale lista dei plugin) |
| Pattern di codice | Firme classiche di web-shell (eval(base64_decode(...)), $_POST[...](), ecc.) in wp-content |
| Utenti | Tutti gli amministratori, e qualsiasi account registrato all'interno della finestra |
| Database | siteurl/home, opzioni e post contenenti script iniettati o codice offuscato |
| Cron | Eventi pianificati di WP-Cron |
| Log | Log di accesso: risposte 5xx al giorno per la finestra rispetto a una baseline fino a 7 giorni precedenti, più gli URL che restituiscono 5xx nella finestra. Log di errore e PHP-FPM: crash dei worker (segfault/bus error). |
| Server | Il crontab dell'utente del sito e le authorized_keys SSH |
| Docker | Ogni container in esecuzione con un binario php: la versione di PHP al suo interno, e se il container esegue ancora una build più vecchia dell'immagine su disco (scaricata ma non ricreata) |
WP-CLI viene eseguito con --skip-plugins --skip-themes così un plugin o tema
con backdoor non può essere eseguito durante il controllo.
Su ogni macchina da controllare:
bash 3.2+, find, grep e sort -V (standard sugli host Linux; funziona anche da una workstation macOS standard)php nel path, per il controllo della versionePer i siti remoti: accesso tramite chiave SSH dalla macchina su cui esegui lo script. Lo script si esegue da solo via SSH; non serve installare nulla sull'host remoto oltre a quanto sopra.
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
Eseguilo come l'utente che possiede i file del sito, quando possibile. Funziona come root
(WP-CLI riceve --allow-root), ma i controlli su crontab e chiavi SSH ispezionano
quelli di root invece di quelli dell'utente web.
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
Per ogni voce user@host:path lo script si connette via SSH, esegue se stesso lì e
riporta il report in php2shell-reports/ sulla tua macchina. Nulla viene
installato sull'host remoto. SSH viene eseguito con BatchMode=yes, quindi è richiesto
un accesso basato su chiave che funzioni senza richiesta di password.
La maggior parte degli host condivisi (Hostinger, SiteGround, Bluehost e simili) offre SSH ma
spesso non ha wp nel path. Verifica con which wp. Se manca,
scarica WP-CLI una volta nella tua directory home e indica allo script dove trovarlo:
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
Senza WP-CLI i controlli sul file system vengono comunque eseguiti e il report indica cosa è stato saltato. In ogni caso, chiedi direttamente all'host quando ha distribuito il PHP corretto, poiché è l'unica cosa che un controllo a livello di account non può vedere.
sites.txt contiene un sito per riga, ovvero un percorso locale oppure user@host:path.
I percorsi remoti relativi sono relativi alla directory home dell'utente SSH. Le righe
che iniziano con # vengono ignorate. Vedi sites.txt.example. Il file è in
.gitignore così i nomi host dei client non finiscono in un commit.
Porte SSH non standard, chiavi e jump host vanno inseriti in ~/.ssh/config:
Host client-a
HostName 203.0.113.10
Port 2222
User deploy
IdentityFile ~/.ssh/client-a
Poi elenca il sito come client-a:/var/www/html.
php -v su un host Docker non ti dice nulla sui container, e scaricare
un'immagine fissa non applica alcuna patch finché il container non viene ricreato da essa. Il sito di un
team è rimasto su 8.3.35 per tre giorni dopo il pull proprio per questo
motivo.
Quando lo script viene eseguito su un host in cui docker ps funziona, il report include una
sezione Docker containers che elenca ogni container in esecuzione che dispone di un
binario php, la versione di PHP al suo interno e se il container è obsoleto
(una build più recente della sua immagine è presente su disco ma il container non è mai stato
ricreato). Ricrealo con docker compose up -d, oppure ferma, rimuovi ed esegui di nuovo il
container.
Per eseguire il controllo completo all'interno di un container, elencalo come target:
docker:wordpress-php-1:/var/www/html