Ovaj web zadatak se možda na prvi pogled čini nemogućim za riješiti Napomena: PHP greške koje možda vidite su dio zadatka http://chal.platforma.hacknite.hr:13005 index.php
Uz zadatak dostupna je i datoteka index.php
Pristupom web stanici vidi se poruka greške da je ključ polja „payload“ nedefiniran prikazano na slici 1.
Analizom izvornog koda zadatka u datoteci index.php vidi se da se „payload“ uzima kao parametar GET zahtjeva. Izvorni kod prikazan je na slici 2.
Može se zaključiti da se payload argument može postaviti kao get parametar, dodavanjem /?payload=<vrijednost> na kraju URL-a stranice. Npr. ako se payload želi postaviti u 13, URL s postavljenim parametrom bi bio:
http://chal.platforma.hacknite.hr:13005/?payload=13
Slanjem ovog parametra pri pristupu web stranici, poruka greške se više ne pojavljuje.
Sljedeći korak je analizirati izvorni kod da se pronađe način kako se manipulacijom parametra payload može dohvatiti flag. Izvorni kod je dovoljno kratak i očito je da je ovo jedini unos nad kojim korisnik stranice ima kontrolu, pa je ovo „točka fokusa“ u zadatku.
Pregledom dokumentacije funkcija mb_strpos i mb_substr, može se vidjeti da mb_strpos u kodu pronalazi indeks prvog pojavljivanja substringa „flag“ u stringu definiranom u payloadu. Funkcija mb_substr vraća substring od indexa 0 do mjesta (indexa) pojavljivanja substringa „flag“ u stringu definiranom u payloadu, koji je poslan kao zadnji argument funkciji u varijabli flag_position. Važno je uočiti da obje funkcije imaju prefiks „mb“ – multibyte. Obje funkcije su iz PHP Multibyte String paketa koji služi za obrađivanje znakova i stringova koji su enkodirani s više od jednog bajta. Dok su ASCII znakovi enkodirani jednim bajtom, UTF-8 znakovi mogu biti enkodirani s dva bajta, što bi moglo narušiti očekivano izvršavanje PHP string funkcija koje očekuju samo znakove enkodirane jednim bajtom, kada bi obrađivale stringove koji sadržavaju Unicode znakove. Više o biblioteci može se pročitati na linku https://www.php.net/manual/en/intro.mbstring.php.
Logika programa je da prvo nalazi poziciju prvog substringa „flag“ u stringu koji je zadan u payloadu, onda od njega radi novi substring, od indexa 0 do indexa pojavljivanja substringa „flag“, te tako efektivno „izrezuje“ ostatak poslanog stringa od indeksa nakon što je pronađen substring „flag“ u njemu. Nakon toga u if provjeri ponovno poziva funkciju mb_strpos s kojom provjerava nalazi li se substring „flag“ u izrezanom stringu, koji će vratiti false ako se ne nalazi „string“ u sanitized varijabli ili će vratiti true, ako mb_strpos pronađe „flag“ i vrati bilo koji broj.
Na prvi pogled, nemoguće je unijeti payload koji će vratiti true u if provjeri, jer će dio stringa od prvog pojavljivanja znakova „flag“ uvijek biti izrezan. Npr. slanjem stringa:
/?payload=AAAAAAflag
U sanitized varijabli će se nakon poziva funkcija nalaziti samo podstring “AAAAAA”, te će if provjera vratiti false.
Kako bi se detaljnije analizirao izvorni kod programa, može se lokalno preuzeti php engine odgovarajuće verzije kao ona koja se koristi u zadatku, može se uočiti da je to verzija “8.1.30” na slici 1. Na Linux-Ubuntu operacijskom sustavu, php engine se može instalirati naredbom:
apt install php8.1-cli
Nakon toga je potrebno instalirati pakete za korištenje mb funkcija sa stringovima odgovarajuće PHP verzije, što se može napraviti naredbom:
apt-get install php8.1-mbstring
Pozicioniranjem u direktorij u kojemu se nalazi index.php, lokalna instanca zadatka se može pokrenuti na portu 8000 naredbom:
php -S localhost:8000
Osim ovog načina, može se koristiti i Docker kontejner, što je preporučeni način korištenja. Dockerizacijom se može olakšati korištenje točnih verzija i paketa kao što se koriste u zadatku, u slučaju da paketi php8.1-cli i php8.1-mbstring budu uklonjeni iz Ubuntu repozitorija.
Na stranici greške može se vidjeti poruka prikazana na slici 3.
Uzevši u obzir da je za server kotišten Apache, a verzija PHP-a 8.1.X, može se koristiti sljedeći Dockerfile, prikazan na slici 4.
Ovaj Docker kontejner se prvo treba sagraditi, pozicionirajući se u isti direktorij kao i Dockerfile, u kojem je također postavljen i index.php, te pokretanjem naredbe:
docker build -t php- zadatak.
Nakon toga se iz izgrađene slike može stvoriti i pokrenuti kontejner naredbom:
Docker run -p 8000:80 php-zadatak
Ovom kontejneru će se onda također moći pristupiti na URL-u:
http://localhost:8000/
Ako se kontejner gradi i pokreće na Linuxu, bit će ga potrebno izgraditi i pokrenuti naredbom “sudo”. ** Može se dogoditi da verzija lokalnog PHP enginea u kontejneru ili izvan, bude npr. 8.1.2 ili 8.1.31, a verzija na platformi bude 8.1.30. Uglavnom ne bi trebale biti značajne promjene između ovih verzija koje bi učinile razliku između rješavanja zadatka lokalno i izvršavanja zadatka na platformi, no može se dogoditi. To se može osigurati ili instalacijom iste verzije, ili provjerom razlika između verzija, da se utvrdi da razlike neće utjecati na izvršavanje programske logike zadatka.
Kada je osiguran jedan od dva opisana načina pokretanja zadatka lokalno (preporučeno je korištenje Dockera), može se modificirati lokalna instanca zadatka, dodajući ispise kako bi se lakše i efikasnije našao prikladan payload, koji će se onda iskoristiti na stvarnoj instanci zadatka. Ispisi omogućuju jednostavniju i efikasniju analizu varijabli i stanja programa pri unosu različitih payloadova.
Primjer modifikacije skripte index.php s odgovarajućim ispisima je prikazan slici 5.
Slanjem payloada:
/?payload=aaaaflagflagflagflag
Na lokalnu instancu korištenjem preglednika:
http://localhost:8000/index.php/?payload=aaaaflagflagflagflag
Može se vidjeti stanje varijabli prikazano na slici 6.
Vidi se da je varijabla “flag_position” postavljena na vrijednost 4, što je indeks prvog pojavljivanja znakova „flag“ u payloadu, i da se u sanitized varijabli nalazi samo string „aaaa“, što je dio stringa payloada prije prvog pojavljivanja znakova „flag“. Funkcija mb_strpos ne vraća ništa, jer se znakovi „flag“ ne nalaze u varijabli sanitized.
Pristup kojim bi se uspješno zaobišlo izrezivanje znakova „flag“ iz varijable payload u varijablu sanitized je nekom manipulacijom funkcija mb_strpos i mb_substr. Resurs koji pruža informacije kako bi se ova tehnika mogla postići dostupan je ovdje: https://www.sonarsource.com/blog/joomla-multiple-xss-vulnerabilities/ Članak objašnjava kako je Joomla CMS imao XSS ranjivost u prethodnim verzijama koja se temeljila na nedostatku u sanitizaciji korisničkog unosa radi korištenja funkcija iz PHP multibyte strings paketa koje se koristi u zadatku. Patch koji je popravio nedostatak je prisutan u PHP verzijama 8.3, 8.4 i daljnjim verzijama, ali je nedostatak i dalje prisutan u PHP verziji 8.1 koja se koristi u zadatku.
Ranjivost se temeljila na razlici u procesiranju nevažećih (eng. invalid) UTF-8 sekvenci između funkcija mb_strpos i mb_substr. UTF-8 (Unicode Transformation Format-8) znakovi mogu biti enkodirani s 1 do 4 bajtova. Po vrijednosti prvog bajta se zna broj bajtova kojima je znak enkodiran. Funkcija mb_substr, nakon što pročita prvi bajt Unicode znaka u kojemu je zapisan broj bajtova s kojim je znak enkodiran, preskače taj broj bajtova, tretirajući ga kao jedan znak. U slučaju kada je ovoj funkciji poslan nevažeći Unicode znak, koji počinje npr. bajtom koji daje informaciju da se znak sastoji od 4 bajtova, ali su samo dva bajta prisutna, nakon čega su npr. znakovi AA -, funkcija će tretirati prva dva bajta nevažećeg unicode znaka zajedno s dva važeća znaka AA kao samo jedan znak. Pravilan Unicode znak bi bio \xf0\x9f\x9f\x9f, no umjesto toga je poslano \xf0\x9fAA, ovdje se 2 znaka A tretiraju kao bajt s vrijednošću 41 (A je 41 u unicodeu) pa se nevažeći skup unosa \xf0\x9fAA ne tretira kao jedan nevažeći unicode znak i 2 važeća unicode znakova A, nego se tretira kao samo jedan unicode znak -\xf0\x9f\x41\x41. Funkcija mb_strpos drugačije radi. Kada pri parsiranju naiđe na nevažeći unicode znak, tretira ga kao samo jedan znak, nakon čega nastavlja normalno brojati slijedeće znakove. Tako se unos \xf0\x9fAA pri korištenju funkcije mb_strpos ne bi tretirao kao samo jedan nevažeći unicode znak, nego kao jedan nevažeći unicode znak \xf0\x9f i 2 važeća Unicode znakova A. Ovo znači da se pri istom unosu koji se sastoji od jednog ili više nevažećih Unicode znakova, tako da je broj bajtova nevažećih Unicode znakova manji nego što je zapisano u prvom bajtu, dobiva drugačije ponašanje ovih dviju funkcija. Za svaku razliku od 2 nedostajuća bajta nevažećeg Unicode znaka, funkcija mb_strpos će indeks naknadnih znakova u unosu „vidjeti“ kao da je veći za vrijednost 2.
Korištenjem unosa \xf0\x9fAAflag, mb_strpos će vratiti da se znakovi „flag“ nalaze na indeksu 3, a funkciji mb_substr će se na indeksu tri nalaziti predzadnje slovo „a“.
Upravo ova razlika između načina na koji ove dvije funkcije procesiraju nevažeći Unicode znak se treba iskoristiti da bi se zadatak riješio.
Testiranjem prethodno spomenutog skupa znakova na lokalnoj instanci, da bi testirali payload \xf0\x9fAAflag, trebamo ga zapisati u formatu: %f0%9fAAflag (umjesto \x za oznaku vrijednosti bajta, koristi se % ). Tako bi payload izgledao ovako:
http://localhost:8000/index.php/?payload=%f0%9fAAflag
Rezultat pri slanju ovog payloada prikazan je na slici 7.
Može se uočiti da je pretpostavljeni scenarij točan, radi razlike od 2 bajta u nevažećem Unicode znaku, sanitized varijabla sadrži dva znaka više, umjesto da su zadnji znakovi „flag“ odrezani, odrezana su samo dva zadnja znaka „fl“.
Kako bi se cijeli skup znakova „flag“ nalazio u varijabli sanitized, potrebno je samo dodati još jedan isti nevažeći Unicode znak na početak payloada, kako bi pomaknuli indeks rezanja payload stringa za još 2 znaka dalje i tako uspjeli sadržati cijeli skup znakova „flag“ u varijabli sanitized. Testiranjem ovog payloada na lokalnoj instanci:
http://localhost:8000/index.php/?payload=%f0%9fAA%f0%9fAAflag
Može se vidjeti da je if uvjet za ispisivanje flaga uspješno zadovoljen, prikazano na slici 8.
Kako bi se riješio zadatak, treba se samo replicirati ova ista tehnika, korištenjem istog ili nekog drugog payloada, na instanci zadatka koja je na platformi.
Unicode:
https://www.sonarsource.com/blog/joomla-multiple-xss-vulnerabilities/
mb:
https://www.php.net/manual/en/book.mbstring.php
mb_strpos:
https://www.php.net/manual/en/function.mb-strpos.php
https://www.geeksforgeeks.org/php-mb_strpos-function/
mb_substr:
https://www.php.net/manual/en/function.mb-substr.php
https://www.geeksforgeeks.org/php-mb_substr-function/