Caso reale anonimizzato · agosto 2026

Un negozio online che macina ordini non si può spegnere. Ogni ora offline è margine che se ne va, e ogni ripristino da backup vecchio riscrive settimane di ordini reali. Qui il problema non era "il sito è giù": il sito girava benissimo. Sotto, però, qualcuno aveva già la chiave di casa.

Cosa avevamo davanti

Il negozio vende accessori tecnici, WooCommerce sopra un tema commerciale, una trentina di plugin. Tra questi, Forminator per i moduli di contatto e i preventivi. Versione vecchia di parecchi mesi.

Quella versione portava dentro CVE-2026-15748: una RCE con punteggio CVSS 9.8. In pratica, un modulo che accetta upload senza controllare davvero cosa gli arriva, e un file .php che finisce dove non dovrebbe mai finire — dentro /wp-content/uploads/. Da lì, chi ha caricato la webshell esegue comandi sul server quando vuole.

Il sito rispondeva 200 su ogni pagina. Il checkout funzionava. Gli ordini arrivavano. Un sito rotto lo vedi subito e corri a ripararlo. Questo no: non rompe niente, aspetta. Per questo una compromissione così fa più paura.

Come l'abbiamo vista

L'audit automatico gira di continuo sui siti che seguiamo. Non guarda solo se la home carica: confronta l'inventario dei file con lo stato precedente, controlla le versioni dei plugin contro i feed di vulnerabilità note, e tiene d'occhio le cartelle dove il PHP non dovrebbe mai comparire.

Alle 09:14 sono scattati due segnali insieme:

  • Forminator a una versione con CVE critica aperta
  • un file .php nuovo dentro /wp-content/uploads/2026/08/, con un nome a caso di otto lettere

Un file PHP negli upload di un WooCommerce non ha una spiegazione buona. Mai. Gli upload contengono immagini di prodotto, fatture, allegati. Codice eseguibile lì dentro non capita per sbaglio: qualcuno ce l'ha messo.

La cronologia (3 ore)

Ora 0 → 0:20 · Isolamento Prima di toccare qualsiasi cosa, tagliamo le gambe alla webshell. Regola nel .htaccess della cartella upload che blocca l'esecuzione di ogni .php: il file resta lì, ma smette di rispondere. L'attaccante perde l'accesso mentre noi lavoriamo con calma. Il sito pubblico non se ne accorge: nessuna immagine di prodotto è un .php.

Ora 0:20 → 0:50 · Backup verificato Backup completo del filesystem e dump del database, con controllo che l'archivio sia integro e apribile — non un file da 0 byte spacciato per backup. Solo con la copia buona in mano si comincia a rimuovere. Qui la sequenza è rigida: mai cancellare niente senza una via del ritorno già provata.

Ora 0:50 → 1:40 · Rimozione Isolata la webshell principale, cerchiamo le sue sorelle. I file modificati nelle ultime settimane vengono passati al setaccio con l'euristica malware: funzioni di esecuzione codice abbinate a base64_decode, stringhe offuscate, variabili esadecimali. Tre file trovati in tutto, tutti dentro gli upload. Ognuno finisce in una copia .MALWARE-<timestamp>.bak accanto all'originale, poi viene neutralizzato. Il database, questa volta, era pulito: nessun utente admin aggiunto, nessun cron malevolo, nessun ordine toccato. La webshell era appena stata piazzata — l'audit l'ha presa prima che venisse usata sul serio.

Ora 1:40 → 2:10 · Patch del vettore Chiudere la webshell senza chiudere la porta da cui è entrata vuol dire riaprire domani. Forminator aggiornato alla versione che corregge la CVE, con verifica della home e del checkout dopo l'update: rollback pronto se qualcosa si fosse rotto. Non si è rotto niente.

Ora 2:10 → 2:40 · Rotazione credenziali La password del database e le salt keys di WordPress vengono cambiate. Chi era entrato una volta potrebbe averle lette, e una password vecchia è un invito. Il cambio delle salt keys butta fuori tutte le sessioni aperte: se restava un cookie rubato, da qui non vale più.

Ora 2:40 → 3:00 · Hardening e verifica La regola no-PHP sugli upload resta permanente, non era un tampone per l'emergenza. Scansione finale del filesystem e del database, controllo che home, catalogo, carrello e checkout rispondano, prova di un ordine di test end-to-end. Tutto verde.

Il conto finale

  • Ordini persi: zero
  • Dati clienti esposti: nessuno (la webshell è stata chiusa prima dell'uso)
  • Downtime pubblico: zero — il negozio ha venduto per tutte e tre le ore
  • Backup vecchio ripristinato: nessuno, il sito è stato pulito sul posto

Questo è il punto che sfugge a chi tratta la sicurezza come un backup da tenere nel cassetto. Ripristinare una copia di due settimane prima, su un negozio con 4.200 ordini, significa cancellare centinaia di transazioni vere per tornare a uno stato "sicuro" che sicuro non era comunque — la falla di Forminator era già lì due settimane prima. Il ripristino avrebbe riportato dentro anche la porta aperta.

Cosa ci si porta a casa

Il vettore qui è un classico che vediamo tornare: un plugin utile, installato e dimenticato, che a un certo punto prende una CVE grave e nessuno lo aggiorna perché "tanto funziona". Forminator è ottimo software; il problema non era il plugin, era la versione ferma.

Tre cose tengono lontano questo scenario, e nessuna richiede che tu ci pensi ogni mattina:

  • Aggiornamenti seguiti, con controllo della home dopo ogni update e rollback automatico se qualcosa cede
  • Cartella upload chiusa al PHP, così anche se un modulo si fa infilare un file, quel file resta muto e non esegue niente
  • Un occhio che confronta lo stato dei file giorno per giorno, perché una webshell si vede dal file nuovo comparso dove non doveva, non dal sito che va in errore

Su questo negozio l'attaccante aveva la webshell pronta e non ha fatto in tempo a usarla. Non per fortuna: perché il file nuovo negli upload è durato meno di venti minuti prima di smettere di rispondere. Tra un incident da tre ore e uno da tre settimane, di solito, cambia una cosa sola: quanto presto qualcuno se ne accorge.

WPSonar tiene quell'occhio aperto sul tuo WordPress e chiude la porta prima che qualcuno entri davvero.