Alerta antivirus era falsă. Ce am găsit în locul ei, nu.
- Client
- Magazin online din România
- Platformă
- PrestaShop (magazin online)
- Infecție
- RCE prin cache Smarty otrăvit, webshell activ
- Durată
- o zi lucrătoare
- Downtime
- zero
Antivirusul folosit de administratorul unui magazin online a semnalat o alertă de redirect malițios. Ne-a contactat pentru o verificare completă. Alerta în sine nu am reușit să o reproducem — nici din browser, nici testând ca Googlebot, nici dintr-un punct de rețea extern. Dar verificarea amănunțită a scos la iveală ceva mult mai grav decât alerta inițială: un atac activ prin execuție de cod la distanță, cu un webshell funcțional încă prezent pe server.
Un fals pozitiv care ascundea un atac real
E tentant să tratezi o alertă nereprodusă drept „fals pozitiv” și să închizi cazul. Noi nu ne oprim acolo: o alertă de securitate, confirmată sau nu, e un motiv suficient pentru o verificare completă a serverului, nu doar a paginii semnalate. De data asta, decizia de a merge mai departe a scos la iveală un compromis real, care nu avea nicio legătură vizibilă cu alerta inițială și care ar fi rămas complet neobservat altfel.
Cum a pătruns atacatorul
Lanțul de atac a pornit de la o injecție SQL, folosită pentru a insera cod PHP direct în tabela din baza de date unde platforma își ține cache-ul de șabloane (Smarty). La următoarea randare a unei pagini, platforma a citit acel cache „otrăvit” și l-a executat printr-un apel eval() aflat chiar în componenta responsabilă de citirea cache-ului — un mecanism legitim al platformei, deturnat complet de la scopul lui.
Vectorul de intrare cel mai probabil: un modul de tip slider, cu o versiune veche, cunoscută cu vulnerabilități istorice, al cărui manager de fișiere rămăsese accesibil public, fără nicio autentificare. Practic, oricine putea trimite fișiere direct pe server prin acel modul.
Ce a scris atacatorul pe server
Codul executat prin eval() a scris un webshell — un fișier cu nume aleatoriu, imposibil de asociat vizual cu ceva suspect — în trei locații diferite ale site-ului, ca redundanță. Logurile serverului confirmă că fișierul nu a fost doar scris, ci și executat: o eroare PHP provenea chiar din prima linie a webshell-ului, la câteva minute după ce fusese creat. Într-o singură zi, logurile conțineau aproape 500 de evenimente asociate direct acestui mecanism de execuție prin cache otrăvit.
Ce nu a fost afectat
Verificarea a acoperit tot ce putea fi atins, nu doar zona evident compromisă:
- front-office curat — homepage, checkout, blog, autentificare, coș, căutare, testate inclusiv ca Googlebot și dintr-un punct de rețea extern
- toate fișierele JavaScript servite către vizitatori — curate
- baza de date — fără alte injecții în conținut, categorii, produse sau configurare
- niciun cont de administrator nou, ascuns sau „fantomă”
- tema activă și codul custom al site-ului — nemodificate de atacator
Această parte contează la fel de mult ca partea găsită: separă un incident circumscris, tratabil rapid, de un compromis extins care ar fi cerut o reconstrucție completă.
Curățarea și închiderea breșelor
Înainte de orice modificare am făcut o copie de siguranță completă a bazei de date și a fișierelor. Apoi:
- cele 3 copii ale webshell-ului au fost șterse;
- un fișier care expunea public toată configurația serverului (
phpinfo()accesibil oricui) a fost eliminat; - un rest de director de instalare, lăsat accidental accesibil din exterior, a fost scos din zona publică a site-ului;
- un director cu permisiuni greșite, scriptibil de oricine, a fost restricționat la valorile corecte;
- managerul de fișiere al modulului vulnerabil, cel care permisese intrarea inițială, a fost blocat;
- parola bazei de date, cheile secrete de autentificare ale platformei și tokenul sarcinilor programate au fost rotite integral;
- conturile de administrator neutilizate de mult timp au fost dezactivate — a rămas activ doar contul folosit efectiv de client.
Un bonus găsit pe drum
În timp ce verificam codul custom al site-ului, am găsit un bug independent de incident, într-un fișier de personalizare a coșului de cumpărături: parametri transmiși greșit către clasa de bază a platformei, care generau o eroare la fiecare încărcare de pagină. Cauza pentru care logurile serverului acumulaseră peste 2 GB în timp — spațiu de disc și zgomot care ar fi îngreunat orice investigație viitoare. L-am reparat pe loc, deși nu avea legătură cu motivul intervenției.
Verificările de la final
O curățare e completă doar dacă poate fi demonstrată. Verificările noastre, toate trecute:
- 0 webshell-uri rămase pe server, pe niciuna dintre cele 3 locații
- 0 fișiere care expun public configurația serverului sau rămășițe de instalare
- cache-ul de șabloane resetat, tipul de caching confirmat pe filesystem, nu bază de date
- 0 înregistrări suspecte rămase în tabela de cache
- 0 conturi de administrator noi, create de atacator
- parolă bază de date, chei secrete și token cron — toate rotite
- front-office, JavaScript și temă — verificate integral, curate
Ce i-am recomandat clientului
- Schimbarea parolei contului de administrator al platformei și a parolei de hosting/FTP. Rotirea cheilor și a parolei bazei de date închide breșa tehnică, dar accesul direct la panoul de administrare și la cont trebuie reînnoit separat, de către client.
- Actualizarea platformei și a versiunii de PHP. Ambele rulau versiuni ieșite din suportul de securitate al producătorului. Curățarea și restricțiile aplicate închid breșa concretă exploatată de această dată, dar nu elimină vulnerabilitățile de platformă care au permis-o — un upgrade la o versiune suportată rămâne necesar pentru a elimina riscul rezidual.
- Monitorizare 1–2 săptămâni, cu verificări simple și repetabile: absența erorilor de tip execuție de cod în loguri, un cache de șabloane care rămâne gol, și absența oricărui cont nou de administrator.
Anexă tehnică — indicatori de compromitere
Pentru administratorii și colegii din industrie care vor să-și verifice propriile magazine PrestaShop:
- Vector inițial
- injecție SQL → cod PHP inserat în tabela de cache Smarty a platformei
- Mecanism de execuție
eval()pe conținutul cache-ului, în componenta custom de citire a cache-ului Smarty (smarty_cacheresource_custom.php)- Vector de intrare probabil
- modul de tip slider, versiune veche, cu manager de fișiere public neautenticat (
upload.php,execute.php,dialog.php— HTTP 200 fără login) - Fișier webshell
- nume format din 32 caractere hexazecimale, scris în 3 locații (rădăcină, modules/, themes/)
- Volum evidență din loguri
- ~95.000 linii într-o singură zi, din care 488 evenimente de execuție prin cache otrăvit (
eval()'d code) - Verificare recomandată
- tip de caching Smarty = filesystem (nu database); tabela de cache a platformei = 0 înregistrări
Site-ul tău trece prin ceva similar?
Îl curățăm și îl securizăm într-o singură intervenție, de la 250 lei, cu garanție 30 de zile. Diagnosticul e gratuit, fără obligații.