This is an old revision of the document!
<source>
Ovaj zadatak ima slojeve
http://chal.platforma.hacknite.hr:14019
</source>
Uz zadatak je dan i izvorni kod.
Pregledom Dockerfilea zadatka može se vidjeti da se radi o PHP zadatku te da se flag kopira u putanju /flag, te da flag datoteka dobiva nasumično generiran naziv u formatu
fl4g_<nasumicnih 16 znakova>
Odlaskom na stranicu zadatka, potrebno je registrirati novi korisnički račun. Nakon logina, prikazuje se stranica na kojoj se može pohraniti jedna slika.
Nakon što se uploada neka mala nasumična slika (npr. whatever.png), dobije se poveznica na lokaciju slike.
Pregledom koda u datoteci user.php može se pronaći kod koji validira da je učitana datoteka zapravo valjana slika.
Kod se sastoji od tri provjere.
1) Provjera ekstenzije datoteke
if (preg_match('/^ph/', $ext)) {
Provjera uzima ekstenziju nakon zadnje točke (npr. .jpg) i provjerava da ne počinje znakovima ph.
Ovu provjeru lako je zaobići dvostrukom ekstenzijom, npr. file.php.jpg.
U tom slučaju se validira .jpg, ali se datoteka sprema bez te provjerene ekstenzije, pa efektivno ostaje .php.
2) Provjera MIME tipa poslanog u zahtjevu
if (strpos($_FILES["file"]["type"], "image/"))
Ova provjera koristi podatak koji klijent šalje i može se jednostavno namjestiti ručno pri slanju datoteke.
3) Provjera "file signature" (serverska provjera)
$info = @getimagesize($_FILES["file"]["tmp_name"]);
$mime = $info['mime'];
...
strpos($mime, "image/") !== 0
Ova provjera čita stvarne “magic bytes” datoteke i određuje je li slika.
Zaobići se može tako da se na početak PHP datoteke dodaju “magic byteovi” slike (npr. .jpg, .png, …). PHP parser ih preskače i izvršava ostatak koda.
—
No, čak i ako se uspješno upload-a PHP datoteka koja prolazi sve provjere, ona se ne bi izvršila, jer se sprema u /uploads/, a tamo postoji .htaccess koji zabranjuje izvršavanje PHP datoteka.
Međutim, učitana datoteka se sprema u poddirektorij /uploads/<specific_user_directory>.
To znači da, ako se može uploadati .htaccess datoteka koja bi zaobišla validaciju, ona bi mogla nadjačati (override) roditeljsku .htaccess konfiguraciju i time dopustiti izvršavanje PHP datoteka unutar korisničkog direktorija.
Problem: korisnik može imati samo jednu datoteku po sesiji — upload nove briše staru, što je prikazano u kodu:
To znači:
ako se upload-a .php datoteka, neće se moći izvršiti zbog ograničenja u .htaccess,
ako se upload-a .htaccess, neće se moći uploadati dodatna .php datoteka jer se prva briše.
—
Postoji ipak način da dvije datoteke završe u istom direktoriju.
Pogledajmo kako se određuje ime korisničkog direktorija:
$userdir = substr(md5($_SESSION['username']), 0, 6) . "/";
Dakle, direktorij se naziva po prvih 6 znakova MD5 sažetka korisničkog imena.
Ako nađemo dva korisnička imena X i Y s istim prvim 6 znakova MD5 hash-a:
MD5(X)[0:6] = MD5(Y)[0:6]
onda će oba korisnika spremati datoteke u isti direktorij.
Jedan može uploadati .htaccess, a drugi .php datoteku.
Pronalazak takvog para je trivijalan brute-force problem — može se riješiti u manje od minute.
Slijedi primjer C programa koji to radi:
<source lang=“c”>
md5_collision_finder6
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <openssl/md5.h>
const char charset[] = “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!()”;
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, “Usage: %s <string>\n”, argv[0]);
return 1;
}
char *input = argv[1];
size_t input_len = strlen(input);
unsigned char input_digest[MD5_DIGEST_LENGTH];
MD51)