Site-urile web și platformele web au devenit cea mai comună modalitate prin care companiile distribuie uneltele pe care le folosim zi de zi. De la platforme de socializare, la aplicații bancare și chiar aplicații de video-conferințe, toate aplicațiile web au la bază aceleași concepte comune gestionate de protocolul HTTP. Datorită răspândirii acestui protocol, testarea aplicațiilor web a devenit vitală în ciclul de dezvoltare al unui proiect, și o abilitate necesară oricărui hacker etic.
Ce este un webserver?
Pentru a înțelege tehnicile de atac întotdeauna trebuie să înțelegem sistemul și modul său de funcționare mai bine decât cel care l-a creat. Deoarece toate aplicațiile web au la bază un server web, apare prima întrebare la care trebuie să ne răspundem: Ce este un server web?
Un server web este un proces care rulează pe un calculator și expune un anumit folder de pe calculatorul respectiv, tuturor clienților care îl accesează. Cu alte cuvinte, web-serverul este gândit să lase clienții să acceseze anumite fișiere de pe calculatorul-server.
Pentru a începe testarea aplicațiilor, vom avea nevoie de un server web de test. Vom continua călătoria folosind server-ul XAMPP. Pentru instrucțiuni de setup și primii pași în configurarea lui, verifică articolul: https://www.ionos.com/digitalguide/server/tools/xampp-tutorial-create-your-own-local-test-server/.
Cum functionează protocolul HTTP?
Protocolul HTTP (Hypertext Transfer Protocol) este metoda de comunicare universală a utilizatorilor și a site-urilor web. Folosind cereri HTTP obținem paginile web pe care intrăm zilnic. Chiar și acest articol a fost încărcat datorită unei cereri de tip HTTP.
Putem studia pașii pe care îi parcurge browser-ul nostru pentru a înțelege felul în care funcționează comunicarea HTTP:
1. Utilizatorul (noi) va încerca să acceseze https://hackout.ro/noutati/ folosind bara de URL
2. Browser-ul va întelege cererea utilizatorului și o va formula în formatul înțeles de protocolul HTTP:
GET /noutati HTTP/2
Host: hackout.ro
3. Cererea va pleca din calculator, va fi redirecționată de router-ul de acasă și va ajunge în cele din urmă în server-ul Hackout:
4. Server-ul va înțelege cererea noastră și ne va oferi un răspuns de tip HTML, care ni se va afișa în browser ca pagina web:
Cererile au o anumită structură:
- GET – este verbul cererii, și îi comunică server-ului ce face cererea noastră:
- vrem informații? – GETvrem să creem o resursă? – POSTvrem să ștergem o resursă? – DELETEvrem să updatăm o resursă? – PUT vrem să vedem ce acțiuni sunt posibile pe o anumita resursă? – OPTIONS
- vrem să vedem ce acțiuni sunt posibile pe o anumita resursă? – OPTIONS
- /noutati – resursa pe care vrem să facem acțiunea
De multe ori, resursa pe care vrem să facem acțiunea reprezintă doar un fișier localizat pe server. De exemplu, avem fișierul profilul-meu.php și vrem fie să preluăm informațiile profilului nostru (folosim GET), fie să updatăm informațiile profilului nostru (folosim PUT).
Însa, există tehnici complexe prin care aceste resurse specificate în cerere se trimit toate către un anumit fișier care mai apoi face acțiunea specificată de utilizator. În această situație, noi vom avea resursa /noutati, nu /noutati.php.
- Host: hackout.ro – header care specifică ce server accesăm și pe ce port (în situația în care nu se specifică, se folosesc porturi standard 80/443)
Aceasta este o introducere scurtă în ceea ce înseamnă protocolul HTTP, pentru mai multe informații verifică resurse precum: https://www.tutorialspoint.com/http/http_overview.htm.
DVWA (Damn Vulnerable Web Application)
Pentru a începe să învățăm cum se face testarea de securitate a unui site web avem nevoie de o țintă. Legislația în vigoare ne interzice atacarea unei resurse deținute de altcineva, fără acordul scris. O testare sigură și legală poate fi făcută pe un laborator deținut de noi, de aceea următorul pas este să ne găzduim o aplicație vulnerabilă pe propria mașină. Vom folosi DVWA (Damn Vulnerable Web Application – https://github.com/digininja/DVWA), setată în web server-ul XAMPP. Aceasta este o aplicație făcută intenționat vulnerabilă și ne va permite să înțelegem vulnerabilități cunoscute.
Vom deschide command prompt-ul în directorul C:\xampp\htdocs și vom clona repository-ul DVWA:
Pentru instrucțiuni despre setup-ul git pe windows și primii pași cu acesta, citește articolul: https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/Step-by-step-guide-to-install-Git-on-Windows-desktop-computers.
Acum putem deschide server-ul apache XAMPP, server-ul MySQL XAMPP și putem accesa pagina http://localhost/DVWA, unde vom primi mesajul: DVWA System error – config file not found. Copy config/config.inc.php.dist to config/config.inc.php and configure to your environment. Asta ne arată că nu am configurat server-ul. Va trebui să copiem fișierul C:\xampp\htdocs\DVWA\config\config.inc.php.dist în C:\xampp\htdocs\DVWA\config\config.inc.php și să îl modificăm astfel:
De asemenea, va trebui să intrăm în PhpMyAdmin (http://localhost/phpmyadmin) și să creem noua bază de date, numită dvwa:
Acum, odată cu reîncărcarea paginii DVWA, vom fi întâmpinați de pagina de instalare:
Vom instala aplicația apăsând pe Create / Reset Database. Odată instalată aplicația, vom fi redirecționați pe pagina de login, unde ne putem loga cu credențialele: admin:password. Ultimul pas va fi să reducem dificultatea provocărilor din nivel imposibil, în nivel ușor. Pentru asta, vom merge la http://localhost/DVWA/security.php și vom selecta din dropdown, nivelul low, apoi submit. Asta ne va permite să ne concentrăm pe înțelegerea conceptului atacurilor.
Ce este un proxy și la ce ajută?
În timpul testării, atunci când aplicația țintă devine mai complexă avem nevoie de unelte care să ne ajute în înțelegerea sistemelor, pentru a găsi vulnerabilități mai interesante. În contextul testării web, orice hacker etic va spune că tool-ul indispensabil este proxy-ul. Acesta este un program care se interpune între browser și server, pentru a capta toate cererile și pentru a face mai ușoară manipularea acestor cereri de către penetration tester.
În figura de mai sus putem vedea cum browser-ul face cererea către webserver, iar ea este captată de proxy, care mai apoi o poate manipula înainte să o trimită într-un final la server-ul web. Prin modalitatea aceasta, noi vom putea să vedem toate endpoint-urile folosite de aplicație, și să testăm fiecare cerere în particular.
Burp Suite-ul este unul dintre cele mai complexe proxy-uri existente pe piață. Are o mulțime de funcționalități printre care:
- mapează site-ul web folosind cererile captate de proxy
- are browser preconfigurat
- suportă dezvoltarea de plugin-uri custom
- oferă un marketplace de plugin-uri pentru aproximativ orice tip de funcționalitate
De asemenea, dezvoltatorii Burp oferă o academie complet gratuită pentru testarea web. Conținutul este mereu updatat cu ultimele tipuri de vulnerabilități și lucrări de cercetare pe tehnologii web de ultimă generație. Academia este creată de Dafydd Stuttard (fondatorul Burp) aceeași persoană care a scris cartea Web Application Hacker’s Handbook (https://portswigger.net/web-security/web-application-hackers-handbook). Pe scurt, individul este un colos în lumea testării securității web și recomand cu căldură toate proiectele dezvoltate de el. Pentru academia PortSwigger vizitează: https://portswigger.net/web-security.
Proxy-ul Burp, fiind un subiect atât de larg, nu vom reuși să îl abordăm în totalitate, ci vom trece doar prin funcționalitățile de bază ale acestuia. Vom începe prin a instala Burp Suite-ul. Vom descărca ediția community de pe site-ul Port Swigger: https://portswigger.net/burp/communitydownload și vom urma wizard-ul de instalare.
Odată deschis Burp Suite-ul, vom avea mai multe opțiuni:
- să deschidem un fișier proiect temporar
- să creem un nou fișier pe disk (opțiune disponibilă doar în varianta Burp Suite Professional)
- să deschidem un fișier proiect (opțiune disponibilă doar în varianta Burp Suite Professional)
Vom merge mai departe cu un proiect temporar. Odată deschis Burp-ul, ne va deschide automat port-ul 8080 local, port pe care îl va folosi ca proxy. Putem verifica acest port în PowerShell, folosind comanda:
netstat -ano | findstr 8080
Burp-ul are o structură destul de simplă și o interfață ușor de folosit. Implicit ne va deschide tab-ul Dashboard. În partea de sus vom avea toate tab-urile disponibile:
- Dashboard – pagina de început, afișează informații despre crawlere și scanări automate
- Target – afișează harta site-ului în mod ierarhic
- Proxy – pagina în care putem vedea toate cererile care trec prin proxy, plus răspunsurile oferite de server
- Intruder – tool prin care putem face cereri automate (este folosit cel mai des la bruteforce)
- Collaborator – (doar pentru Professional) pagina care ne ofera acces la un server third party pentru atacuri care necesită un al treilea server pentru exfiltrări de date
- Repeater – tool care ne permite preluarea unei cereri și manipularea acesteia pentru testarea diverșilor parametrii
- Decoder – tool care ne permite encodarea/decodarea în diferite formate (URL, baza 64, ASCII, HTML entities, etc.)
Vom începe prin a înțelege cum interceptăm cererile prin proxy. Vom naviga pe tab-ul Proxy unde vom vedea:
Apăsând pe Open Browser ni se va deschide un browser modificat (un fork al proiectului Chromium, vezi https://www.chromium.org/chromium-projects/), pe care îl putem folosi pentru a naviga pe aplicația pe care vrem să o testăm.
În continuare, vom deschide XAMPP-ul (Apache și MySQL) și vom naviga pe aplicația noastră DVWA folosind link-ul http://localhost/DVWA. Odată ce s-a încărcat pagina, putem naviga pe sub-tab-ul ‘HTTP history’ pentru a vedea ce cereri a făcut server-ul:
Apăsând pe prima cerere, vom putea observa în partea din stânga a ecranului cererea noastră HTTP completă, și în partea din dreapta răspunsul oferit de server:
Observăm că răspunsul dat de server este 302 (vezi statusurile cererilor HTTP: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status), și ne redirecționează către http://localhost/DVWA/login.php folosind header-ul ‘Location’ (vezi: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location).
Dacă dăm click pe a doua cerere, către http://localhost/DVWA/login.php, vom vedea ca răspunsul dat de server este de tip HTML, adică pagină de login afișată de browser.
În continuare vom încerca să ne logam cu credențialele admin:password. Putem observa cerea POST de login în sub-tab-ul HTTP History:
Credențialele sunt transmise într-un format tip application/x-www-form-urlencoded (vezi: https://medium.com/@codingscenes/application-x-www-form-urlencoded-and-multipart-form-data-are-two-different-formats-for-3678a10073e9). Odată ce server-ul a validat credențialele, ne va seta o cheie de sesiune în cookie, pe care o va folosi pentru a verifica dacă suntem sau nu logați:
Set-Cookie: PHPSESSID=opqn3td1ai1rq3clr7rgiuagb2; expires=Fri, 02-Feb-2024 10:28:47 GMT; Max-Age=86400; path=/; HttpOnly; SameSite=Strict
Putem încerca să modificăm această cerere folosind tab-ul Repeater. Vom da click dreapta pe cererea POST de login și Send to Repeater:
Apoi vom naviga pe tab-ul Repeater, unde vom vedea cererea noastră:
Aici vom putea modifica cererea cum dorim, apoi să observăm răspunsul server-ului pentru cererea noastră modificată. Vom modifica username-ul în admin123 pentru a observa cum se comportă server-ul când îi oferim credențiale invalide, apoi vom apăsa pe Send pentru a trimite cererea și a observa răspunsul:
Server-ul ne va seta un nou ID de sesiune în cookie și ni se oferă oportunitatea să ne lăsăm redirecționați de server pentru a-i observa răspunsul. Vom da pe Follow redirection și vom observa că server-ul ne-a trimis înapoi pe pagina de login. Vom folosi sub-tab-ul render pentru a vedea HTML-ul procesat de browser-ul implicit Burp:
Acestea au fost cele mai triviale exemple de utilizare Burp, însă software-ul este foarte complex și are multe funcționalități. Recomand oricărei persoane care vrea să se dezvolte în aria testării web să citească articolele de utilizare Burp din academia Port Swigger (https://portswigger.net/web-security/essential-skills/using-burp-scanner-during-manual-testing) și să refacă ambele vulnerabilități din capitolele următoare folosind proxy-ul Burp.
Exploatarea primei vulnerabilități
Command Injection
Injecțiile de comandă sunt unele dintre cele mai periculoase vulnerabilități pe care le poate avea un site web. Acestea apar din cauza lipsei de validare a inputului de la utilizator (ca orice altă vulnerabilitate de altfel). Să presupunem că avem o aplicație făcută să trimită mesaje ICMP (ping) către o altă mașină. Aplicația are nevoie de un singur input, IP-ul mașinii țintă. Să navigăm în DVWA, pe secțiunea de Command Injection (http://localhost/DVWA/vulnerabilities/exec/):
Înainte de a încerca să facem orice atac asupra aplicației, întotdeauna recomand să alocăm timp pentru a ne imagina cum este făcută aplicația în spate. Asta ne ajută să înțelegem cum funcționează sistemul în interiorul server-ului, lucru care ne va da o perspectivă din interior, făcând astfel atacul mai ușor.
Având o aplicație care are nevoie de un input de la utilizator, vom presupune că inputul este preluat de un cod scris într-un limbaj de programare (în cazul nostru PHP), așa că, într-un fel sau altul, sigur avem o linie similară cu:
variabila ip = ia_input_utilizator(“IP”)
Apoi, aplicația va executa comanda de sistem PING, pentru a trimite pachete ICMP către ținta specificată de utilizator, deci vom mai avea o linie similara cu:
afiseaza(executa_comanda_sistem(“ping -c 5 ” + ip))
Dacă vom încerca să ne trimitem pachete ICMP către localhost, vom avea următorul output în aplicație:
Având acest pseudocod conceptual și înțelegând modul de funcționare a aplicației, primul lucru la care ne gândim este, ce s-ar întâmpla dacă în loc să specificăm doar IP-ul, am încerca să injectăm și o comandă de sistem? Comanda de sistem executată arată așa:
ping -c 5 {IP}
În locul IP-ului, am putea să specificăm 127.0.0.1 && dir pentru a încerca să injectăm comanda dir. Asta ne va permite accesul la sistemul de fișiere al aplicației.
Încercând acest payload, vom avea urmatoarea surpriză:
Output-ul dat de aplicație ne sugerează că am reușit să executăm comanda dir pe sistem. Am reușit să obținem execuție de cod pe mașină (RCE).
Un ultim pas ar fi să analizăm impactul acestei vulnerabilități. Vom folosi aplicația https://www.first.org/cvss/calculator/3.0#CVSS:3.0 pentru a estima un cod CVSS (vezi articolul https://www.beyondsecurity.com/blog/cvss-explained).
- Attack Vector – de unde putem să exploatăm vulnerabilitatea? Aceasta vulnerabilitate nu necesită vreo apropiere a atacatorului, așa că vom specifica Network. Atacatorul poate fi oriunde în lume, având acces la internet poate exploata vulnerabilitatea.
- Attack Complexity – cât de complex este atacul? Această întrebare poate părea subiectivă, însă se referă la numărul de pași pe care trebuie să îi urmeze atacatorul până la exploatarea finală. Vom seta acest câmp pe Low, deoarece atacul nostru are un singur pas.
- Privileges Required – de ce privilegii are nevoie atacatorul pentru a exploata vulnerabilitatea? Fiind nevoie de logare în aplicație, cu privilegii de admin, acest câmp va trebui setat pe High. Atacatorul are nevoie de privilegii de administrator pentru a exploata aplicația.
- User Interaction – avem nevoie ca un utilizator victimă să declanșeze atacul? În situația noastră, vulnerabilitatea nu necesită prezența vreunui utilizator victimă, sau declanșarea payload-ului de acesta. Vom seta acest câmp pe None.
- Scope – exploatarea vulnerabilității duce la compromiterea altor componente? Această întrebare se referă la felul în care atacul afectează sistemul pe alte nivele. În cazul nostru, exploatarea vulnerabilității duce la schimbarea scopului, din nivelul aplicației în nivelul execuției comenzilor de sistem, deci acest câmp trebuie setat pe Changed.
Triada CIA – cum afectează vulnerabilitatea noastră componentele: confidențialitate, integritate, disponibilitate? În cazul nostru, exploatarea vulnerabilității duce la compromiterea întregului sistem, adică atacatorul:
- are acces la toate informațiile server-ului – compromiterea totală a confidențialității.
- are acces de scriere în sistem – compromiterea totală a integrității sistemului.
- are acces de închidere a aplicației, a sistemului etc. – compromiterea totală a disponibilității sistemului.
Toate câmpurile CIA trebuie setate pe HIGH. Asta ne va rezulta într-o vulnerabilitate estimata CVSS 9.1, adică o vulnerabilitate critică:
Injecție SQL
Injecțiile SQL reprezintă vulnerabilități foarte interesante care pot duce la compromiterea totală a sistemului. Pentru a înțelege cum funcționează o astfel de vulnerabilitate trebuie mai întâi să înțelegem lanțul de apeluri într-o aplicație web, și cum se accesează baza de date.
În figura putem observa cum clientul (noi) face o cerere către site, iar site-ul face o comandă internă către baza de date. Comanda către baza de date se numește query. Pentru a construi niște baze solide SQL, verifică: https://www.w3schools.com/sql/sql_intro.asp. În continuare vom presupune că avem o bază în construcția de query-uri SQL.
Într-o injecție SQL, clientul reușește prin câmpurile pe care le controlează în cererea făcută către server, să injecteze comenzi în query-ul trimis de aplicația web către baza de date. Să presupunem următorul pseudocod:
variabila user_id = ia_input_utilizator(“user_id”)
afiseaza(query_sql(“SELECT * FROM users WHERE id = ‘“ + user_id + “’”))
În pseudocodul de mai sus putem observa faptul că valoarea din user_id se concatenează direct la restul query-ului. Asta ne va permite nouă să folosim un payload similar cu:
‘ OR 1=1; #
Rezultând astfel comanda:
SELECT * FROM users WHERE id = ‘’ OR 1=1; #’
Putem observa cum vom folosi # pentru a comenta apostroful care era deja concatenat de aplicație. Astfel, am schimbat workflow-ul aplicației și am forțat-o să ne afișeze toți utilizatorii, în loc să îl afișeze doar pe cel cu un anumit ID.
Să explorăm această vulnerabilitate în DVWA. Vom deschide XAMPP-ul (Apache și MySQL) și vom naviga pe secțiunea SQL Injection (atenție să nu fie Blind) sau direct pe link-ul: http://localhost/DVWA/vulnerabilities/sqli/.
Avem o aplicație care caută utilizatorul în funcție de ID-ul specificat de noi în câmpul User ID:
Dacă vom încerca să afișăm utilizatorul cu ID-ul 1, vom avea:
Acum am putea începe procesul de fuzzing. Fuzzing-ul este procesul prin care testăm cu diferite input-uri pentru anumite vulnerabilități. Fiecare vulnerabilitate are anumite payload-uri prin care ne putem da seama dacă un anumit câmp este sau nu vulnerabil. Pentru injecțiile SQL, vom folosi input-ul apostrof ‘ deoarece de cele mai multe ori, dezvoltatorii de aplicații web încadrează input-urile date de utilizator între apostrof, astfel, adăugarea încă unui apostrof va genera o eroare de sintaxă în query-ul SQL:
SELECT * FROM users WHERE id = ‘’’;
Alte posibile input-uri de fuzzing pentru injecțiile SQL pot fi:
- ghilimele “ – unii dezvoltatori încadrează în ghilimele în loc de apostrof
- punct și virgulă ; – unii dezvoltatori nu încadrează nici în ghilimele nici în apostrof anumite câmpuri, rămânând o singură variantă de a genera o eroare, folosind punct și virgulă
Pentru mai multe payload-uri organizate pe tipuri de baze de date, verifică acest repository: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection.
După ce vom încerca apostroful ca input, vom vedea acest mesaj:
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''''' at line 1
BINGO, înseamnă că am descoperit o vulnerabilitate de tip SQL prin fuzzing. Acum putem încerca să injectăm o comandă pentru a afișa toți utilizatorii. Mesajul de eroare ne oferă informații precum tipul bazei de date, care este MariaDB, deci putem construi un payload pentru sintaxa specifică MariaDB:
‘ OR 1=1; #
Executând acest query, vom vedea toți utilizatorii:
Acum avem o injecție SQL confirmată, însă întotdeauna când găsim o vulnerabilitate, scopul nostru este să încercăm să creștem impactul cât mai mult, să creștem gravitatea problemei aproape de critic sau poate chiar critic (acolo unde se poate). Folosind o tehnică de injecție SQL de tip UNION, putem scrie un fișier în server-ul nostru web, fișier pe care mai apoi îl putem folosi pentru a obține RCE (Remote Code Execution) pe webserver.
Întâi va trebui să enumerăm numărul de coloane folosite în tabela users din DVWA. Operația UNION ne permite să facem reuniunea a două seturi de date DOAR DACĂ AU ACEEAȘI STRUCTURĂ. Vom folosi o tehnică de trial and error cu GROUP BY pentru a enumera câte coloane avem:
Operația este executată cu succes, lucru care înseamnă că există coloana 1 (deoarece server-ul a încercat să grupeze toate datele în funcție de prima coloană). Vom repeta procesul, de data asta cu a doua coloană:
' GROUP BY 2; #
Operația va fi executată fără niciun mesaj de eroare, lucru care înseamnă că există și a doua coloană în query-ul nostru. Vom repeta acum procesul pentru a treia coloană:
' GROUP BY 3; #
De data asta vom primi mesajul:
Unknown column '3' in 'group statement'
Această eroare înseamnă că în query-ul nostru, nu există coloana 3, astfel setul nostru de date rezultat va avea doar două coloane. Știind acest lucru putem face o reuniune cu două elemente:
Query-ul va executa cu succes, va căuta în baza de date un user cu ID gol și va reuni setul de date rezultat cu setul nostru scris în UNION, adică 1,2. Mai departe, putem lucra la o reuniune care să ne ajute să scriem un fișier PHP în webserver-ul nostru. Să analizăm următorul payload:
' UNION SELECT 1, "<?php system($_GET['cmd']); ?>" INTO OUTFILE "C:/xampp/htdocs/DVWA/shell.php"; #
Vom avea o reuniune cu un set de date format din două elemente:
- 1 – doar un numar pentru a umple o coloană goală
- <?php system($_GET[‘cmd’]); ?> – un shell PHP care ne va permite să oferim ca parametru URL o comandă și o va executa folosind funcția system (vezi https://www.php.net/manual/en/function.system.php)
Mai departe folosim un redirector INTO OUTFILE folosit pentru a scrie setul de date rezultat într-un fișier. Vom scrie în fișierul C:/xampp/htdocs/DVWA/shell.php știind că aplicația noastră are ca root calea C:/xampp/htdocs/DVWA. Astfel, vom crea un fișier nou pe care îl putem accesa folosind link-ul: http://localhost/DVWA/shell.php. Pentru mai multe informații despre cum funcționează webshell-ul PHP, vezi: https://secure.wphackedhelp.com/blog/web-shell-php-exploit/.
Acum că am reușit să creem webshell-ul, tot ce ne rămâne este să îl accesăm și să executăm o comandă de sistem. Vom specifica comanda CMD în parametrul cmd al URL-ului: http://localhost/DVWA/shell.php?cmd=dir.
Putem observa că am obținut structura fișierelor completă din server-ul nostru, ceea ce înseamnă că sistemul este compromis. Ultimul pas este să estimăm impactul CVSS. Am obținut RCE, exact ca în exemplul de injecție de comandă, avem nevoie de aceleași privilegii, deci impactul CVSS va fi același, 9.1 (vulnerabilitate critică).