Una scansione dei file pulita non è un sito pulito. Gli script iniettati vivono comodamente dentro i contenuti: il corpo di un articolo, un widget, una riga di impostazioni, un'opzione del tema. Reinstallare l'applicazione sostituisce i file e lascia ognuno di quelli esattamente dov'era.
Cercalo
SELECT * FROM wp_posts
WHERE post_content LIKE '%<script%'
OR post_content LIKE '%eval(%'
OR post_content LIKE '%base64_decode%'
LIMIT 50;
SELECT option_name, LEFT(option_value, 120) FROM wp_options
WHERE option_value LIKE '%<script%' LIMIT 50;
La stessa idea vale per qualunque schema: le colonne di testo che i visitatori vedono, più la tabella delle impostazioni. Cercare un tag script dentro contenuti che non dovrebbero mai contenerne ne trova la maggior parte.
Oppure cerca direttamente in tutto il dump
Più rapido che tirare a indovinare fra le tabelle, e funziona anche su uno schema che non conosci.
mysqldump -u user -p dbname > /tmp/scan.sql
grep -n -E '<script|eval\(|base64_decode|document\.write' /tmp/scan.sql | head -40
shred -u /tmp/scan.sql
Ripuliscilo
- Prima fai un backup del database — stai per lanciare istruzioni UPDATE su contenuti in produzione.
- Sistema una riga a mano — conferma la stringa esatta, e che toglierla lasci un contenuto valido.
- Poi il resto, con una portata stretta —
UPDATE wp_posts SET post_content = REPLACE(post_content, '<script src="//bad.example"></script>', '') WHERE post_content LIKE '%bad.example%'; - Confronta il conteggio prima e dopo — una REPLACE che tocca più righe di quante ne abbia trovate la tua SELECT significa che il criterio è troppo largo.
E poi chiudi la porta
I contenuti vengono iniettati attraverso qualcosa: un plugin vecchio, una password di amministrazione rubata, una cartella di caricamento scrivibile. Ripulire le righe senza trovare quel qualcosa significa che tornerà nel giro di giorni. Vedi Ripulire un sito violato.