Activitatea echipelor de pentest este deopotrivă interesantă și intensă, marcată pe de o parte de întrebări continue despre când și cum inteligența artificială va schimba modul în care lucrează, dar mai ales de realitatea deloc romantică a termenelor, a reglementărilor, și mai ales a unui mediu din ce în ce mai dinamic (vedeți articolul referitor la Threat Landscape Report Enisa pentru referințe).
De aceea, în discuțiile interne avem, nu de puține ori, analize despre situații și probleme identificate, ca parte a procesului intern de training cross-departamental. Despre un astfel de caz am scris azi, folosind o combinație de terminologie tehnică și non-tehnică, în încercarea de a vă detalia ce înseamnă cu adevărat munca de pentesting și care sunt beneficiile colaborării cu echipe experimentate.
Într-un audit recent asupra unei platforme open-source folosite în producție de clientul nostru, am descoperit șase vulnerabilități zero-day, niciuna dintre ele documentată public la momentul analizei. Nu vorbim despre configurări greșite sau despre igiena de securitate a clientului, ci despre defecte în codul platformei însăși: probleme pe care le moștenește oricine rulează acel software, indiferent cât de disciplinat își administrează infrastructura.
Concluzia: înlănțuite, aceste vulnerabilități permit compromiterea completă a serverului și a tuturor datelor găzduite pe el. Iar cele mai grave nu necesită un cont de administrator. În unele cazuri, pe instalările cu configurația implicită, nu necesită niciun cont.
Ce am găsit
Cele șase probleme acoperă trei clase distincte:
- Două execuții de cod la distanță (RCE). Prima, prin evaluarea unei expresii server-side ca JavaScript, vectorul cel mai grav din întregul lot. A doua, un eval injection prezent în versiuni mai vechi, remediat între timp prin eliminarea componentei vulnerabile.
- O scriere arbitrară de fișiere, escaladabilă la RCE.
- Trei vulnerabilități Server-Side Request Forgery (SSRF) distincte, dintre care una nu este doar un vector nou, ci un bypass al unui control anti-SSRF deja existent în aplicație.
Severitatea se întinde de la High la Critical, cu scoruri CVSS 3.1 între 7.4 și 9.9. Ceea ce transformă acest set dintr-o listă de bug-uri într-un scenariu de dezastru este accesibilitatea: cele mai grave sunt exploatabile de utilizatori autentificați fără privilegii administrative: exact profilul pe care orice platformă multi-tenant îl consideră „low trust”.
RCE-ul de 9.9: când o funcționalitate devine o portiță
Vârful de gravitate este o RCE cu scor CVSS 9.9, accesibilă oricărui utilizator autentificat.
Rădăcina problemei e o funcționalitate care, pe hârtie, pare inofensivă: aplicația permite atașarea unor expresii condiționale la pașii unui flux de lucru. În practică, aceste expresii sunt evaluate pe partea de server ca JavaScript complet, în interiorul unui context vm.runInNewContext() din Node.js.
Aici intervine o concepție greșită care apare surprinzător de des în cod de producție: vm din Node nu este un sandbox de securitate. Documentația oficială Node o spune explicit. Este un mecanism de izolare a contextului, nu o barieră împotriva codului ostil, iar diferența costă, în cazul de față, întreaga infrastructură.
Fiindcă șirul stocat de utilizator nu este niciodată validat, un atacator poate evada din context printr-un lanț clasic: globalThis.constructor.constructor îl duce la constructorul Function al realm-ului gazdă și, de acolo, la obiectul process, de unde execută comenzi arbitrare la nivel de sistem de operare. Codul rulează cu privilegiile contului de serviciu, în procesul de control-plane. Practic, aceasta înseamnă acces direct la secretele din configurație: chei de semnare, credențiale de bază de date, chei de vault, și escaladare de la utilizator obișnuit la administrator fără nicio altă vulnerabilitate. Un singur defect, controlul întregului sistem.
Scrierea de fișiere: un detaliu subtil de Python cu efecte majore
Complementar RCE-ului, scrierea arbitrară de fișiere exploatează un comportament pe care mulți dezvoltatori Python îl întâlnesc fără să-l observe. Numele fișierului de destinație, preluat nesanitizat din input, este transmis către os.path.join(). Ce puțini știu este că os.path.join() ignoră complet directorul de bază atunci când primește o cale absolută. Este un comportament documentat, dar contraintuitiv.
Consecința: un atacator poate suprascrie un modul Python aflat pe calea de import a aplicației și, implicit, poate obține execuție de cod la următoarea încărcare a modulului. Un al doilea drum către aceeași destinație: control deplin.
SSRF-ul care ocolește propriul zid de apărare
Dintre cei trei vectori SSRF, unul merită atenție specială fiindcă demonstrează cum un control de securitate existent poate oferi o falsă siguranță.
Aplicația avea deja o validare anti-SSRF de tip allowlist. Problema: validarea era aplicată o singură dată, asupra întregului bloc multi-linie de input, în timp ce parserul care efectuează efectiv cererea extrage doar primul host. Rezultatul e o discrepanță clasică între ce se validează și ce se execută: orice URL plasat pe a doua linie trece nevăzut de allowlist și este descărcat fără verificare.
În medii cloud, acest gap se traduce direct în extragerea credențialelor IAM de la endpoint-ul de metadata, adică, din nou, de la o simplă cerere de aplicație la cheile împărăției.
De ce contează dincolo de această situație punctuală
Fiecare dintre aceste defecte spune ceva mai larg decât „o platformă anume are un bug”:
- Funcționalitatea flexibilă este suprafață de atac. Orice loc în care aplicația evaluează input-ul utilizatorului ca și cod, fie el JavaScript, un template sau o expresie, trebuie tratat ca RCE potențială până la proba contrarie.
- Un control de securitate netestat e uneori mai periculos decât absența lui, pentru că oprește vigilența. Bypass-ul SSRF a fost posibil tocmai fiindcă exista un allowlist care „părea” suficient.
- Zero-day nu înseamnă doar „bug necunoscut”, ci risc pe care nicio patch-uire promptă nu-l acoperă, pentru că patch-ul nu există încă. Aici, valoarea nu stă în a reacționa rapid la advisory-uri publice, ci în a le găsi înaintea atacatorilor.
Pentru orice organizație care rulează software open-source în producție, adică, practic, pentru toate, mesajul e simplu: dependențele nu vin cu garanție de securitate, iar un audit ofensiv orientat spre lanțuri de exploatare, nu spre bug-uri izolate, este singura modalitate de a vedea sistemul așa cum îl vede un atacator.
De la „ce-ar putea merge prost” la certitudine
Cele șase vulnerabilități de mai sus nu au fost găsite scanând automat după semnături cunoscute. Au fost găsite gândind ca un atacator: urmărind cum se leagă funcționalitățile între ele, unde input-ul devine cod și unde un control de securitate „existent” nu acoperă de fapt ce ar trebui.
Exact acesta este tipul de analiză pe care echipa FORT îl aduce în fiecare misiune.
Dacă organizația ta rulează software open-source sau aplicații custom în producție și vrei să știi ce vede un atacator înainte să o afle pe calea grea, scrie-ne aici, te putem sprijini cu:
- Testare de penetrare: testare manuală, condusă de specialiști, care depășește scanările automate și urmărește lanțuri de exploatare reale, așa cum le-ar exploata un atacator
- DevSecOps: integrarea securității în ciclul de dezvoltare, ca defectele să fie prinse înainte să ajungă în producție
- Audit și consultanță în securitate cibernetică: evaluarea posturii de securitate și recomandări concrete de remediere
- Răspuns la incidente și SOC: detecție, monitorizare și reacție rapidă atunci când o amenințare devine reală.