fl4g_
Odlaskom na stranicu zadatka, potrebno je registrirati novi korisnički račun, nakon logina prikazuje se stranica na kojoj se može pohraniti jedna jedina slika. \\
{{ phpmadness:slika2.png?nolink&500 | Slika 2 - stranica zadatka}}
Nakon što se uploada neka mala nasumična slika (npr. **whatever.png**), dobije se poveznica na lokaciju slike. \\
{{ phpmadness:slika3.png?nolink&500 | Slika 3 - poveznica na lokaciju slike}}
Pregledom koda u datoteci **user.php** može se pronaći kod koji validira da je učitana datoteka zapravo slika. \\
{{ phpmadness:slika4.png?nolink&500 | Slika 4 - user.php – validacije učitane slike}}
Kod se sastoji od tri provjere, prva provjera je samo provjera ekstenzije učitane datoteke, provjerava se tako da se uzme dio naziva datoteke nakon zadnje točke, npr. **.jpg**, te se provjerava da ta ekstenzija ne počinje znakovima **ph**, što je definirano ovim REGEX pravilom:
if (preg_match('/^ph/', $ext)) {
Ovu provjeru lako je zaobići, tako da se stave dvostruke ekstenzije, npr.
file.php.jpg
U kojem bi slučaju ova provjera uzela samo **.jpg**, nakon čega sprema datoteku bez provjerene ekstenzije, što bi značilo da bi samo druga ekstenzija, **.php**, ostala.
Druga provjera, provjerava da MIME tip poslan u zahtjevu odgovara slici:
if (strpos($_FILES["file"]["type"], "image/"))
Ovu provjeru je lagano zaobići namještanjem ove vrijednosti na korisničkoj strani pri slanju datoteke.
Treća provjera:
$info = @getimagesize($_FILES["file"]["tmp_name"]);
$mime = $info['mime'];
...
strpos($mime, "image/") !== 0
Provjerava datoteku na serverskoj strani, čitajući njezin "file signature" ili "magic bytes" i prema njima određuje je li učitana datoteka slika ili nije.
I ovu provjeru moguće je jednostavno zaobići, tako da se napravi PHP datoteka s kodom koji se želi izvršiti na serveru, na početku koje se dodaju "magic byteovi" odnosno "file signature" neke slike, npr. .jpg, i u slučaju da bi PHP parser čitao ovu datoteku, on bi ignorirao ove byteove i nakon toga uspješno pročitao i pokrenuo PHP kod.
No postoji problem, čak i da se uploada datoteka s PHP kodom koja zaobilazi sve ove provjere, ona se i dalje ne bi izvršila, zato što će se učitati pod putanjom **/uploads/**, a u **/uploads** direktoriju postoji **.htaccess** file, koji zabranjuje izvršavanje svim PHP datotekama. \\
{{ phpmadness:slika5.png?nolink&500 | Slika 5 - .htaccess u /uploads direktoriju}}
No uploadana datoteka će se nalaziti u poddirektoriju od **/uploads**, nalazit će se u:
/uploads/<specific_user_directory>
Što znači da ako je moguće uploadati **.htaccess** file, koji bi također zaobišao provjere da je uploadana datoteka slika, taj učitani **.htaccess** file bi nadjačao (eng. override) roditeljski **.htaccess** file. Time bi bilo moguće definirati vlastita **.htaccess** pravila u direktoriju pojedinog korisnika.
No problem je da svaki korisnik smije imati samo jednu učitanu datoteku po korisničkoj sesiji, i učitavanjem nove datoteke, stara datoteka se briše i samo nova čuva, što je prikazano u kodu na slici 6. \\
{{ phpmadness:slika6.png?nolink&500 | Slika 6 - user.php – logika učitavanja datoteke korisnika}}
To znači da ako korisnik učita **.php** file koji zaobilazi sve provjere, on neće biti dopušten zbog **.htaccess** konfiguracije u naddirektoriju, a ako uspije učitati **.htaccess** datoteku koja overridea krovnu konfiguraciju, neće biti moguće učitati **.php** datoteku koja bi iskoristila tu novu konfiguraciju koja dopušta izvršavanje PHP koda.
No postoji način za učitati dvije datoteke u isti direktorij, kako bi se mogla učitati prvo **.htaccess** datoteka koja bi dozvolila izvršavanja PHP koda i onda druga datoteka koja bi sadržavala PHP kod, koji bi se izvršio i koristio za čitanje flaga.
Odgovor leži u logici koja određuje u koji direktorij će se pohraniti korisnikova učitana datoteka, što je određeno u ovoj liniji koda:
$userdir = substr(md5($_SESSION['username']), 0, 6) . "/";
Ova linija koda određuje da će se korisnikov direktorij nazvati kao prvih 6 znakova MD5 sažetka korisnikovog korisničkog imena (usernamea).
To znači da ako možemo pronaći dva korisnička imena, koja oba imaju istih prvih 6 znakova njihovih MD5 sažetka, odnosno
MD5(X)[0:6] = MD5(Y)[0:6]
gdje su X i Y dva različita usernamea, svaki korisnik može uploadati jednu datoteku u isti direktorij, tako da korištenjem jednog korisnika se može uploadati **.htaccess** datoteka, a korištenjem drugog korisnika datoteka sa PHP kodom, čije je izvršavanje omogućeno **.htaccess** datotekom.
Pronalazak para usernameova čijih prvih 6 znakova MD5 hash algoritma se podudaraju je jednostavan problem i čistim bruteforceom se može pronaći vrlo brzo, ispod minute (implementacijom u C-u).
Sljedeće je jedan primjer C koda koji pronalazi ovo rješenje.
korisnik1 i BQuxIUGH
Preostaje problem kako učitati **.htaccess** datoteku, koja će uspješno proći kroz provjere da je učitana datoteka slika, dok će se i dalje dobro parsirati kao ispravan **.htaccess** file. Pri parsiranju PHP fileova, nepoznati byteovi će se samo preskočiti, te je ovo trivijalan problem kod datoteke s PHP kodom, dok je u slučaju **.htaccess** datoteke, ovo zapravo najteži dio zadatka.
.htaccess datoteka koja bi koristila ovaj pristup bi se mogla zvati **.htaccess.jpg**.
Postoje dva rješenja.
==== .wbmp ====
Prvo se zasniva na činjenici da se pri parsiranju .htaccess datoteke, null bajtovi na početku preskaču bez errora, te se file dalje normalno parsira, dok 4 null bajtovi na početku datoteke PHP prepoznaje kao file signature .wbmp datoteke, koja je slika, te se tako može napraviti „pseudo-poliglot” datoteka koja PHP-ovu provjeru uvjerava da je .wbmp slika, dok je i dalje .htaccess file koji se može uspješno parsirati. Datoteka može imati sadržaj:
disable_functions = exec,system,passthru,shell_exec,proc_open,popen,pcntl_exec
No i dalje i s ovim ograničenjem ima dosta načina za pronaći flag datoteku i pročitati ju i bez ovih zabranjenih funkcija.
Primjer datoteke s PHP kodom, koja bi bila odobrena prethodnom **.htaccess** datotekom i pronašla flag datoteku te je pročitala i ispisala se može zvati **readFlag.php3.jpg** i sadržavati file signature bilo koje slike, npr. koristeći tehniku .wbmp, te nakon toga potrebni PHP kod:
";
echo htmlspecialchars(file_get_contents($path));
echo "