secaudit.eu
Incidenti

WordPress hackerato di nuovo? Come trovare la backdoor

WordPress viene hackerato di nuovo dopo la pulizia? Dove si nascondono le backdoor: auto_prepend_file, mu-plugins, plugin falsi, cron, siti vicini, accessi aperti.

Di , AngelForce OÜ 8 min di lettura

Anche in: English Deutsch Français Español Polski Português Türkçe

Se WordPress viene hackerato di nuovo dopo la pulizia, quasi certamente l'attaccante ha ancora una via d'accesso. O una backdoor è sopravvissuta alla pulizia, oppure la falla sfruttata la prima volta è ancora aperta. Una copia nuova del core e qualche file cancellato non risolvono né l'una né l'altra cosa.

Questo playbook parte dal presupposto che una pulizia sia già stata fatta. Passa in rassegna i punti in cui di solito qualcosa sopravvive, nell'ordine in cui viene eseguito il codice, con un controllo specifico per ciascuno. Si chiude con il passaggio che interrompe davvero il ciclo.

Perché WordPress viene hackerato di nuovo dopo la pulizia

Le cause sono due, e spesso sono presenti entrambe. La prima è la persistenza. Raramente gli attaccanti lasciano una sola backdoor: ne piazzano diverse in punti diversi, così che, se ne trovate una, le altre continuino a funzionare. La seconda è un punto d'ingresso ancora aperto: un plugin vulnerabile, una password rubata o un sito vicino nello stesso account di hosting.

I tempi offrono un indizio. Una reinfezione a poche ore dalla pulizia indica di solito una backdoor sopravvissuta o un'attività pianificata che riscrive i file. Una reinfezione dopo giorni o settimane fa pensare piuttosto a una falla aperta che qualcuno, o qualche bot, ha trovato di nuovo.

Le falle aperte vengono trovate in fretta. Lo State of WordPress Security in 2026 di Patchstack è un rapporto di un fornitore e va letto come tale. Secondo il rapporto, per le vulnerabilità sfruttate più intensamente il tempo mediano ponderato fino allo sfruttamento di massa è stato di 5 ore, e circa la metà delle vulnerabilità ad alto impatto è stata sfruttata entro 24 ore. Lo stesso rapporto indica che il 46 % delle vulnerabilità non aveva alcuna correzione disponibile al momento della divulgazione pubblica. Un plugin non aggiornato non è un rischio minore da rimandare alla settimana prossima.

Diagramma: il ciclo di reinfezione e i quattro livelli in cui si nascondono le backdoor
Ogni pulizia che trascura un livello alimenta il ciclo. Solo chiudere il punto d'ingresso e sostituire tutti i segreti lo interrompe.

Prima di cercare: copiare le prove

Prima di cancellare altro, fate una copia completa dei file e del database e salvate i log di accesso del web server. Poi registrate i timestamp dei file, per poterli confrontare in seguito con le voci dei log:

cd /path/to/site
find . -type f -printf '%T@ %TY-%Tm-%Td %TH:%TM %p\n' | sort -n > ~/file-times-$(date +%F).txt
wp db export ~/evidence-db-$(date +%F).sql

L'esportazione del database contiene dati personali: spostate queste copie fuori dal server, in un archivio sicuro. In caso contrario la ricerca dei backup più avanti le elencherà, e potrebbero essere lette o cancellate per errore. Tenete un semplice elenco, con data, di ogni file, utente e job cron che rimuovete: serve a individuare il punto d'ingresso. Se erano coinvolti dati personali, serve anche perché l'art. 33, par. 5, del GDPR impone di documentare qualsiasi violazione dei dati personali, anche quelle che non vengono notificate. I passaggi completi di contenimento sono nel nostro piano di rimozione del malware da WordPress.

Backdoor che si attivano prima del caricamento di WordPress

L'impostazione PHP auto_prepend_file esegue un file prima di ogni richiesta PHP, prima che WordPress venga caricato e prima che qualsiasi plugin di sicurezza possa vederlo. È proprio per questo che piace agli attaccanti. Può essere impostata in php.ini, in un file .user.ini in una qualsiasi directory, oppure con php_value in .htaccess su Apache con mod_php. Cercate in tutto l'account:

grep -Rn --include='*.ini' --include='.htaccess' -E 'auto_(prepend|append)_file' ~ 2>/dev/null
php --ini

php --ini mostra quali file di configurazione carica il PHP a riga di comando. Il PHP del web server può usarne altri, quindi controllate anche le impostazioni PHP nel pannello di hosting. Qualsiasi valore che punta a un file sconosciuto è un riscontro da trattare. Annotate il percorso, conservate una copia del file, poi rimuovete l'impostazione. Le modifiche a .user.ini possono richiedere qualche minuto per diventare effettive, perché PHP mette in cache questi file.

Controllate poi wp-config.php. Un file pulito ha un unico require_once ABSPATH . 'wp-settings.php'; verso la fine e nulla di eseguibile sopra il blocco di commenti iniziale. Cercate qualsiasi altra cosa:

head -n 5 wp-config.php
grep -nE 'eval\(|base64_decode|gzinflate|str_rot13|include|require' wp-config.php
awk 'length > 300 {print FILENAME": line "NR" is "length" chars"}' wp-config.php

Confrontatelo quindi con diff rispetto a una copia affidabile presa da un vecchio backup. Se non ne avete una pulita, confrontate la struttura con wp-config-sample.php. Include aggiuntivi, righe lunghissime e codice prima del blocco di commenti non hanno ragione di esserci.

I nascondigli in wp-content: mu-plugins e plugin falsi

Must-use plugin e drop-in

Tutto ciò che si trova in wp-content/mu-plugins viene eseguito automaticamente a ogni richiesta. WordPress li mostra solo in una scheda Must-Use separata della schermata Plugin, i file PHP nelle sottocartelle non compaiono affatto e nessuno di essi può essere disattivato dalla bacheca. Molti siti non hanno alcun must-use plugin. Alcuni hosting ne installano uno per la cache o per la gestione.

ls -la wp-content/mu-plugins
wp plugin list --status=must-use
wp plugin list --status=dropin

Anche i drop-in vanno controllati: object-cache.php, advanced-cache.php e db.php direttamente in wp-content. Sono legittimi quando li hanno messi lì la vostra cache o il vostro hosting. Se nulla di ciò che avete installato li spiega, considerateli sospetti.

Cartelle di plugin false o contraffatte

Una cartella di plugin senza un'intestazione valida non compare in WordPress, ma i suoi file PHP possono comunque essere richiamati direttamente tramite URL. Confrontate ciò che c'è sul disco con ciò che WordPress conosce:

ls -1 wp-content/plugins | sort > on-disk.txt
wp plugin list --field=name | sort > registered.txt
comm -23 on-disk.txt registered.txt

index.php e i plugin a file singolo come hello.php compariranno nell'output. Tutto il resto merita un'occhiata. Fate attenzione ai nomi che imitano plugin reali, per esempio con un trattino in più, un numero di versione nel nome della cartella o diciture come «wp-security-core». Poi eseguite wp plugin verify-checksums --all: copre solo i plugin ospitati su WordPress.org. Per i plugin premium, scaricate una copia nuova dal fornitore e confrontatela con diff -r. Per il core di WordPress, seguite la nostra guida alla verifica dei checksum di WordPress.

File PHP modificati di recente

find wp-content -type f -name '*.php' -mtime -30 -printf '%TY-%Tm-%Td %p\n' | sort
find wp-content -type f -name '*.php' -ctime -30

Se non avete ancora reinstallato il core, un altro utile punto di riferimento è -newer wp-includes/version.php. Gli attaccanti possono falsificare le date di modifica con touch, ma non possono falsificare con la stessa facilità la data di cambiamento (-ctime): eseguite quindi entrambi i comandi. I file PHP nella cartella uploads sono trattati a parte nella nostra guida sulle web shell in wp-content/uploads.

Persistenza nel database e nello scheduler: cron, amministratori, password applicative

Gli eventi di WP-Cron risiedono nel database e sopravvivono alla pulizia dei file. Elencateli:

wp cron event list --fields=hook,next_run_relative,recurrence

Ogni hook dovrebbe appartenere al core o a un plugin che conoscete. Per un hook sconosciuto, cercate nel codice con grep -Rn "hook_name" wp-content. Se il codice che lo registra si trova in un mu-plugin o in un file che non sapete spiegare, avete trovato una backdoor. Un hook senza codice corrispondente può significare che la backdoor è stata rimossa ma l'evento è rimasto. Eliminate l'evento con wp cron event delete hook_name.

Controllate poi il lato server: crontab -l per l'utente di hosting, più la sezione cron del pannello di hosting. Un job che esegue curl o wget, o che scrive nella radice web, può ripristinare in silenzio una backdoor ogni notte.

Seguono gli account. Questo comando elenca tutti gli amministratori, poi le password applicative di ciascuno:

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
for id in $(wp user list --role=administrator --field=ID); do echo "User $id"; wp user application-password list "$id"; done

Le password applicative continuano a funzionare anche dopo un cambio di password, quindi una voce sconosciuta è grave quanto un amministratore sconosciuto. La verifica completa è nella nostra guida ai ruoli utente di WordPress.

Infine, cercate script iniettati nei contenuti e nelle impostazioni. Sostituite wp_ se il prefisso delle vostre tabelle è diverso:

wp db search 'script src=' wp_options wp_posts --stats
wp db search 'atob(' wp_options wp_posts --stats
wp db query "SELECT option_name, LENGTH(option_value) AS size FROM wp_options WHERE autoload IN ('yes','on','auto','auto-on') ORDER BY size DESC LIMIT 20;"

Le prime due ricerche trovano gli include di script esterni e la decodifica base64 che il JavaScript iniettato usa spesso. La query elenca le opzioni autoload più grandi: un nome di opzione sconosciuto con un valore molto grande merita un esame più attento.

Il plugin di audit di secaudit.eu riporta questi indicatori di compromissione in un unico posto: amministratori nascosti, voci cron sospette, spam nel database e discrepanze nei checksum del core. Rileva e segnala. Le decisioni sulla rimozione restano vostre → secaudit.eu

Il resto dell'account di hosting: siti vicini e vecchie copie

L'hosting condiviso spesso ospita più siti sotto un unico utente di sistema. Una copia di staging infetta, un'installazione dimenticata in /old/ o /test/, oppure uno ZIP di backup nella radice web reinfetteranno il sito appena pulito. Elencate tutto ciò che c'è nell'account:

find ~ -name wp-config.php 2>/dev/null
find ~ -type d -name wp-includes 2>/dev/null
find ~ -type f \( -name '*.zip' -o -name '*.tar.gz' -o -name '*.sql' \) 2>/dev/null

Pulite tutti i siti dell'account nello stesso momento, oppure eliminate quelli che nessuno usa. Se tutti i siti condividono un unico utente SFTP e un unico utente del database, qualsiasi sito compromesso può scrivere su tutti gli altri. Dove possibile, separateli in utenti distinti o in account di hosting distinti.

Chiudere il punto d'ingresso e sostituire tutto in una volta

Individuate il punto d'ingresso nei log di accesso, attorno al timestamp della prima infezione. Cercate richieste POST verso file anomali e verso gli endpoint del plugin sospetto:

grep '"POST ' access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -30

Aggiornate il plugin vulnerabile. Se non esiste una correzione o il plugin è abbandonato, sostituitelo. Se non vi serve, eliminatelo. Disattivarlo non basta, perché i file restano sul disco e possono ancora essere raggiunti tramite URL.

Poi sostituite tutti i segreti in un'unica sessione, dopo la pulizia e non prima. Se lo fate prima, una backdoor sopravvissuta legge semplicemente i nuovi valori.

  • Tutte le password degli amministratori WordPress e ogni password applicativa
  • Password del database: cambiatela nel pannello di hosting, poi aggiornate DB_PASSWORD in wp-config.php
  • Accesso al pannello di hosting, password SFTP e chiavi SSH (rimuovete le chiavi che non riconoscete)
  • Chiavi API memorizzate nei plugin (pagamenti, SMTP, CDN, archiviazione dei backup)
  • Chiavi di sicurezza e salt: wp config shuffle-salts

Ripetete gli stessi controlli dopo 24 ore e di nuovo dopo 7 giorni, confrontando i risultati con i vostri file di prova. Se riappare un mu-plugin, un evento cron o un file modificato, c'è ancora qualcosa che non avete trovato.

FAQ

Quando conviene smettere di fare da soli?

Fermatevi alla seconda reinfezione, oppure subito se i dati dei clienti possono essere stati esposti o se il sito è un negozio con ordini. A quel punto rivolgetevi a professionisti della risposta agli incidenti. Se sono coinvolti dati personali, verificate anche se si applica la notifica entro 72 ore all'autorità di controllo prevista dal GDPR. La nostra guida sulla notifica della violazione entro 72 ore spiega da quando decorre il termine.

Ripristinare un backup è sufficiente?

Solo se il backup è precedente alla prima backdoor e avete chiuso il punto d'ingresso. Altrimenti ripristinate il problema insieme al sito.

Cambiare tutte le password ferma gli attacchi?

No. Una backdoor sopravvissuta non ha bisogno di una password. Sostituite le credenziali dopo la ricerca, non al suo posto.

Questo articolo è un'informazione di carattere generale e non costituisce consulenza legale.

Controlla il tuo sito

Security Audit & Compliance è il plugin WordPress di secaudit.eu: cerca malware, spam nascosto, file core modificati e codice vulnerabile, e collega i risultati all’art. 32 del GDPR, all’art. 21 della NIS2 e alla OWASP Top 10.

Scopri cosa controlla il plugin
Melike Görel

Melike Görel scrive le guide di secaudit.eu sulla sicurezza di WordPress e sulla conformità alle norme UE per AngelForce OÜ, l’azienda di software di Tallinn che sta dietro a secaudit.eu.

Realizzato con l’aiuto dell’IA e verificato prima della pubblicazione. Questo articolo è un’informazione generale e non una consulenza legale.