This is an old revision of the document!
Zadatak s Hacknite platforme - Zapamti me
Naši kolačići koriste najnovije sigurnosne tehnologije http://chal.platforma.hacknite.hr:14003
Uz zadatak je dan i izvorni kod.
Stranica ima samo Register/Login i stranicu kojoj se pristupa nakon što se ulogira s korisnikom, a koja ispisuje samo pozdrav.
Pregledom source koda vidi se da postoji *Dockerfile* u kojem je definirana ENV flag varijabla, ali nema ništa što na prvi pogled pokazuje ranjivost.
Osim *Dockerfilea*, jedini drugi izvorni kod je server.js, u kojem se nalazi sav kod koji pokreće stranicu.
Pri analizi koda vidi se da je adminov username “administrator0”:
const ADMIN_NAME = 'administrator0';
Također se vidi da stranica koristi *secret* varijablu koja je sastavljena od 22 nasumična bajta, generirana pomoću *crypto.randomBytes* modula, što znači da bi trebala biti sigurna.
Također, *FLAG* je definiran kao varijabla.
Nakon toga slijedi inicijalizacija baze podataka.
U inicijalizaciji baze podataka vidi se da svaki korisnik ima ID, username, password, token, *isAdmin* varijablu (koja je po defaultu 0) te vrijeme kada je korisnik stvoren.
Također se vidi da se administratorski korisnik postavlja s vrijednošću 1 za *isAdmin*.
Ključan dio koda je način na koji se generira token.
Token se generira kao string koji se sastoji od *secret* varijable, *usernamea* i *ID-a* korisnika, a zatim se ubacuje u *bcrypt.hash* funkciju, koristeći i *BCRYPT_SALT*.
*BCRYPT_SALT* varijabla se generira u *initializeAuth* funkciji koja se poziva samo jednom pri pokretanju servera. To znači da će ta varijabla biti ista tijekom cijelog životnog vijeka aplikacije.
Nadalje, u administratorskom tokenu nalazi se i *adminId*, koji se generira pomoću sljedećih linija koda:
17. linija koda: const UUID_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341'; 54. linija koda: const adminId = uuidv5(ADMIN_NAME, UUID_NAMESPACE);
Funkcija *uuidv5* je deterministička — za isti par *username* i *namespace* svaki put će generirati isti UUID.
To znači da u generaciji administratorskog tokena (slika 3) od tri varijable — *secret*, *ADMIN_NAME* i *adminId* — poznajemo *ADMIN_NAME* (“administrator0”) i možemo lokalno rekreirati isti *adminId* kao onaj koji koristi aplikacija.
Budući da su i *secret* i *BCRYPT_SALT* varijable konstante dok aplikacija radi, token administratora uvijek se računa na isti način.
Ključna činjenica zadatka temelji se na ponašanju funkcije:
bcrypt.hash
Naime, *bcrypt.hash* uzima samo prvih 72 znaka danih kao ulaz i iz njih generira sažetak, dok sve znakove nakon 72. odbacuje. Zato dva unosa s istih prvih 72 znaka daju isti hash, bez obzira na razlike iza 72. znaka.
Pregledom *tokenString* varijable, koja se ubacuje u *bcrypt.hash* i generira administratorski token, vidi se njezina duljina.
Varijabla se sastoji od tri stringa: *secret*, *ADMIN_NAME* i *adminId*.
- *secret* = 22 znaka - *ADMIN_NAME* = 14 znakova (“administrator0”) - *adminId* = 36 znakova (UUID)
Zbrajanjem 22 + 14 + 36 dobiva se 72, što je točno broj znakova koje *bcrypt.hash* uzima u obzir.
—
Sada pogledajmo kako se generira token pri stvaranju novog korisnika, što se događa u “/register” endpointu.
Može se vidjeti da se token za novog korisnika generira na isti način kao i administratorski token.
Koristi se ista *secret* varijabla i ista *BCRYPT_SALT* vrijednost kao i kod generiranja administratorskog tokena.
Jedine dvije različite vrijednosti su *username* i *userId*.
Na *userId* nemamo utjecaj, ali *username* možemo kontrolirati.
Budući da je *secret* duljine 22, a *bcrypt.hash* uzima samo prvih 72 znaka, ako unesemo *username* duljine 50, on će “istisnuti” *userId* iz dijela koji *bcrypt.hash* koristi. To znači da *userId* uopće neće ući u generiranje korisničkog tokena.
Na taj način imamo potpunu kontrolu nad 50 znakova (nakon *secret*), koji će sudjelovati u generiranju tokena.
Pošto su *secret* i *BCRYPT_SALT* isti pri generiranju oba tokena (admin i user), ako uspijemo manipulirati tih 50 znakova da budu isti kao kod administratora, dobit ćemo identičan token.
Da bi to bilo moguće, mora vrijediti:
naš_username = admin_username + adminId
Vrijednosti *admin_username* i *adminId* su poznate ili se mogu rekonstruirati.
—
Preostaje pokrenuti aplikaciju lokalno kako bismo saznali *adminId* i napravili korisnika s odgovarajućim imenom.
Možemo dodati liniju koda koja će ispisati *adminId*:
Pokretanjem programa naredbom:
node server.js
vidjet ćemo *adminId*, koji će biti isti kao i na produkcijskom serveru.
a1653d44-7862-5db2-b60a-afce3f20ed74
Sada znamo sve potrebne informacije za generiranje istog tokena kao administrator.
Analizom *token middleware* funkcije vidi se kako se koristi token:
Middleware dohvaća token iz kolačića, traži prvog korisnika u bazi s tim tokenom i postavlja sesiju kao tog korisnika.
Pošto se uzima prvi user u bazi, a administrator je uvijek prvi kreirani korisnik, bilo koji kasniji korisnik s istim tokenom automatski postaje administrator.
—
Dakle, rješenje je kreirati novog korisnika s *usernameom*:
administrator0a1653d44-7862-5db2-b60a-afce3f20ed74
Možemo dodati još nekoliko znakova na kraj (koji ionako neće ući u prvih 72 znaka *bcrypt.hash* funkcije) da izbjegnemo grešku ako korisničko ime već postoji.
Dakle, svaki korisnik s *usernameom* oblika:
administrator0a1653d44-7862-5db2-b60a-afce3f20ed74<bilo_koji_znakovi>
imat će isti token kao administrator.
—
Pri pokušaju logina vidi se da postoji ograničenje maksimalnog broja znakova u korisničkom imenu (14 znakova), što bi onemogućilo ovu tehniku.
No to ograničenje nije implementirano na serverskoj strani, nego samo u HTML kodu (klijentskoj strani), pa se lako može zaobići — brisanjem dijela HTML-a prikazanog na slici 11. ili korištenjem *BurpSuite* alata.
Sada se može stvoriti korisnički račun koji će imati isti token kao administrator, što je prikazano na slici 12.