Cele mai frecvente vulnerabilități de API pe care le găsim în pentesturi

Cele mai frecvente vulnerabilități de API pe care le găsim în pentesturi

API-urile sunt locul în care trăiesc de fapt aplicațiile moderne. Browserul e doar o coajă subțire; logica reală, datele și deciziile de autorizare stau în spatele unei suprafețe tot mai mari de endpointuri REST și GraphQL. Acolo găsim și cele mai grave probleme în testele de penetrare. OWASP API Security Top 10 (ediția 2023) este o hartă bună a terenului, iar după destule proiecte aceleași câteva categorii revin constant.

Vulnerabilitățile pe care le vedem cel mai des

Broken Object Level Authorization (API1) rămâne cel mai frecvent finding serios. Endpointul verifică faptul că ești autentificat, dar nu și că înregistrarea cerută îți aparține. Schimbi /api/invoices/1042 în /api/invoices/1043 și citești factura altcuiva. E banal de testat și dureros de des întâlnit în practică.

Broken Object Property Level Authorization (API3) este vărul mai discret: obiectul e al tău, dar API-ul te lasă să citești sau să scrii câmpuri pe care n-ar trebui să le atingi — să transformi "role": "user" în "role": "admin" într-un corp JSON, sau să primești înapoi un câmp care expune datele altui utilizator. Mass assignment și expunerea excesivă de date locuiesc aici.

Broken Authentication (API2) și Broken Function Level Authorization (API5) completează problemele de autorizare: gestionare slabă a tokenilor, endpointuri de admin accesibile utilizatorilor obișnuiți, verbe HTTP care ocolesc verificările impuse de interfață.

Server-Side Request Forgery (API7) și Unsafe Consumption of APIs (API10) sunt zonele în care API-urile sunt păcălite să acceseze sau să aibă încredere în lucruri pe care n-ar trebui. Orice endpoint care acceptă un URL, o cale de fișier, un parametru „template" sau „include" și acționează pe baza lui pe server merită o privire foarte atentă. Exact acest fir vrem să-l tragem în continuare.

Un lanț ilustrativ: de la un file inclusion „minor" la RCE

Aceasta este o demonstrație generică, anonimizată — niciun client real, niciun fel de date confidențiale. Clasa de vulnerabilitate este veche, dar o găsim în continuare în aplicații .NET moderne, motiv pentru care merită explicată. O păstrăm la nivel de metodologie, defensiv, nu ca exploit gata de copiat.

Imaginează-ți un endpoint care primește un parametru — să-i spunem template, report sau include — și încarcă un fișier pe server pentru a construi răspunsul. Întrebarea de recon este simplă: ajunge input necontrolat la un API de fișiere sau de încărcare?

Întâi local file inclusion. Îndreptăm parametrul spre fișiere cunoscute de pe server — propriul web.config al aplicației, fișiere sursă, o cale care dezvăluie connection strings. Dacă răspunsul reflectă conținutul lor, am dovedit că input controlat de atacator ajunge la o funcție de citire de fișiere. În sine, un scanner ar putea nota asta „mediu": dezvăluire de informații.

Apoi remote inclusion. Îndreptăm același parametru spre o resursă găzduită de atacator. Dacă aplicația o descarcă și o procesează, endpointul face cereri externe fără nicio listă de surse permise — comportamentul apropiat de SSRF pe care OWASP îl semnalează la API7. Acum atacatorul controlează nu doar ce fișier este citit, ci și conținutul lui.

Apoi sink-ul periculos. Pasul critic este să găsim unde acel conținut controlat de atacator este interpretat, nu doar citit. În .NET, sink-ul clasic este deserializarea nesigură. Formattere precum BinaryFormatter, LosFormatter și ObjectStateFormatter fac deserializare polimorfică nerestricționată, iar Microsoft este tranșant: BinaryFormatter este nesigur și nu poate fi făcut sigur — din .NET 9 aruncă excepție la utilizare. Dă unuia dintre aceste formattere octeți controlați de atacator și lanțuri de gadgeturi disponibile public transformă deserializarea în execuție de cod. Un view sau template server-side care ajunge compilat, ori o scriere într-un director executabil al aplicației, obțin același rezultat.

Impactul. Înlănțuirea unui bug „minor" de inclusion cu un sink periculos duce la execuție de comenzi în identitatea application pool-ului — un finding critic construit integral din slăbiciuni individual banale. Este exact ideea pe care o demonstrează pentestul și pe care un scanner nu o poate: riscul stă în lanț, nu în linia izolată.

Cum îl închizi

Allow-listing strict al inputului este fundamentul: nu lăsa niciodată inputul utilizatorului să decidă o cale de fișier sau un URL. Dezactivează deserializatoarele periculoase — preferă System.Text.Json, XmlSerializer sau DataContractSerializer, iar dacă un formatter vechi e inevitabil, constrânge-l cu o listă de tipuri permise printr-un SerializationBinder. Nu compila și nu executa conținut furnizat de utilizator. Rulează application pool-ul cu privilegii minime și adaugă filtrare de egress, ca un serviciu compromis să nu poată ajunge liber pe internet. Fiecare control rupe o altă verigă din lanț.

Mapat înapoi pe OWASP, această singură poveste atinge Broken Object Property Level Authorization, Server-Side Request Forgery și Unsafe Consumption of APIs — trei din cele zece, într-un singur endpoint.

Concluzia

Majoritatea vulnerabilităților de API pe care le raportăm nu sunt exotice. Sunt lacune de autorizare și inputuri nevalidate pe care scannerele le subevaluează, pentru că pericolul apare doar când slăbiciunile se combină. Un test de penetrare există tocmai pentru a găsi aceste lanțuri înaintea unui atacator. Dacă API-urile tale au crescut mai repede decât testarea ta de securitate, merită să închizi această diferență deliberat.

Înapoi la blog