This is an old revision of the document!
Second order SQL injection
Second order SQL injection je podskup SQL injection ranjivosti, koju je kompliciranije iskoristiti za izvršavanje napada, ali je i teže detektirati ovu ranjivost.
Uobičajeni SQL injection je prisutan u slučajevima kada se korisnički unos direktno koristi za konstrukciju SQL upita, dok second order SQL injection se izvršava tako da se neki korisnički unos prvo pohrani negdje na server ili u bazu podataka, a zatim se pozivanjem neke druge funkcionalnosti, koja koristi taj pohranjeni korisnički unos, izvršava SQL injection.
Jednostavan primjer bi bio aplikacija gdje se unos iz forma za registraciju korisnika koristi u SQL upitu za dodavanje korisnika u bazu koji dobro interpretira unos korisničkog imena kao string i nije ranjiv na SQL injection.
Korisnik registracijom kao korisničko ime unosi vrijednost:
" user' or 'a' = 'a' -- "
čime se samo stvara novi korisnik s tim korisničkim imenom.
Kada bi taj korisnik koristio funkcionalnost promjene lozinke, gdje se pri promjeni lozinke koristi SQL upit u koji se ubacuje korisničko ime korisnika kojemu se mijenja lozinka, bez ikakvih zaštita, izvršio bi se SQL injection za vrijeme SQL upita u kojemu se odabranom korisniku mijenja lozinka.
Primjer nezaštićenog koda za izmjenu lozinke bio:
"UPDATE users SET password ='" + novi_password + "' WHERE username ='" + korisnicko_ime + "' and password= '" + stari_password +"'"
(pretpostavka je da je korisnikov unos passworda dobro filtrira jer se izravno koristi kao vanjski unos, dok se korisničko ime dohvaća iz baze te se zato pogrešno ne koristi na siguran način)
Pri pokretanju ovog koda za prethodno registriranog korisnika s unosom novog passworda „moj_novi_password23“, kod koji bi se izvršio bi bio:
UPDATE users SET password= 'moj_novi_password23' WHERE username= 'user' or 'a' = 'a' -- ' and password= 'stari_password'
Dvije crtice koje se pojavljuju u upitu su oznaka za otvaranje komentara u mnogim relacijskim bazama podataka.
-- tekst nakon ove oznake se ne evaluira kao SQL upit nego se smatra komentarom
Raadi toga se dio upita nakon pojave te oznake se više ne tretira kao SQL kod, pa je zato prethodni upit ekvivlantan sljedećem:
UPDATE users SET password= ’moj_novi_password23’ WHERE username= 'user' or 'a' = 'a'
Ovaj upit bi svim korisnicima promijenio password u
moj_novi_password23
Primjer - Zadatak s Hacknite platforme - e-Trgovina Union
Lokalna trgovina je odlučila napraviti svoju web stranicu na kojoj kupci mogu pratiti svoju povijest kupovine. Korisnici se mogu sami registrirati na tu stranicu, i pri svakoj fizičkoj kupnji u trgovini mogu dati svoje korisničko ime kako bi se njihova povijest kupovine unijela u bazu podatka. Stranica bi mogla biti korisna svim sadašnjim i budućim kupcima trgovine! Flag je u formatu CTF2021[brojevi] http://chal.platforma.hacknite.hr:10010
Pri rješavanju zadatka, prvo što nam se nudi je forma za registraciju.
Analizom izvornog koda ovog zadatka, koji je također dostupan u zadatku, vidimo da se SQL upit tako formiran da se nad njime ne mogu izvršavati SQL injekcije, što je prikazano u slici ispod.
Naime, kod koristi tzv. parametrizirane upite koji onemogućavaju SQL injekciju.
Analizom ostalih dijelova koda, može se vidjeti da se parametrizirani upiti koriste na svim mjestima, osim u funkciji sadrzaj(), unutar user.php datoteke.
Ovdje vidimo da se parametar
$_SESSION["user"]
ne ubacuje u SQL upit na sigurni način, odnosno da postoji ranjivost. No parametar
$_SESSION["user"]
se već nalazi unutar sustava, taj parametar odgovara korisničkom imenu s kojim smo se registrirali.
Način na koji možemo iskoristiti ovu ranjivost jest da napravimo račun s korisničkim imenom koje će iskoristiti ranjivost unutar ovog SQL izraza, kada će nam sustav prikazivati sadržaj naše povijesti.
Kada napravimo korisnički račun s normalnim korisničkim imenom i prijavimo se u sustav, na početnoj stranici, tablica koja prikazuje povijest proizvoda je prazna.
Kao prvi pokušaj iskorištavanja ranjivosti možemo probati napraviti korisnički račun kojemu će korisničko ime biti
aaa' or '1' = '1' –
Nakon stvaranja korisničkog računa s ovim korisničkim imenom, vidimo da se sada unutar tablice povijesti proizvoda nalaze proizvodi svih ostalih korisnika u sustavu, kao što je prikazano na slici ispod.
Ovo znači da smo uspjeli izvršiti SQL injekciju nad sustavom i da sada znamo iskoristiti ranjivost. Sada još samo moramo pronaći način kako napraviti SQL injekciju s kojom ćemo dohvatiti one podatke koji nas zanimaju.
Iz koda znamo da u bazi postoji tablica users, s poljima username, password i role. Taj dio koda je vidljiv na slici 21.
Sada možemo pokušati izvršiti SQL injection, gdje će korisničko ime biti:
aaaa' UNION SELECT username, password FROM users –
Kako bismo dohvatili imena i lozinka korisnika iz baze. Izvršavanjem ove SQL injekcije, dobivamo error koji je prikazan na slici ispod.
Ovaj error nam govori da broj parametara u SELECT izrazu koji smo konstruirali ne odgovara broju parametara u SELECT izrazu s kojim radimo UNION.
Iz koda znamo da izraz odgovara sljedećem:
SELECT * FROM povijest WHERE korisnik= ' aaaa' UNION SELECT username,password FROM users --
Iako se u tablici za prikaz proizvoda na početnoj stranici nalaze samo dva stupca, SQL izraz kojeg mi manipuliramo, iz kojega se naknadno prikazuju ta dva stupca na stranici ima više od dva stupca, no ne možemo točno znati koliko, budući da u djelu koda
SELECT * FROM povijest
jedino znamo da se dohvaćaju svi stupci iz tablice povijest, ali ne znamo točno koliko
U sljedećem pokušaju možemo probati izvršiti SQL injection, gdje ćemo imati koristiti tri stupca u SELECT izrazu. Primjer takvog pokušaja jest stvaranje korisničkog računa s korisničkim imenom:
aaaa' UNION SELECT username,password,password FROM users –
Vidimo da smo uspješno dobili hasheve lozinki svih korisnika u sustavu. Također vidimo da se na stranici prikazuje samo zadnji parametar koji smo unijeli u našem SELECT izrazu, koji je u ovom slučaju password. Ako želimo prikazati više informacija, možemo koristiti SQL-ov operator konkatenacije: „ || “, da bi prikazali više vrijednosti u jednom stupcu, na primjer da bi prikazali username i password odvojene znakom dvotočke, za zadnji parametar trebamo umjesto samo password, unijeti „ username|| ': '||password “, koristeći ovu vrijednost za zadnji parametar, finalno korisničko ime kojim ćemo izvršiti SQL injection koji nam treba jest:
aaaa' UNION SELECT username,password, username|| ': '||password FROM users --
Sada smo uspješno izvršili SQL injection pomoću kojega smo dohvatili i prikazali korisnička imena i hasheve lozinka svih korisnika u sustavu, Možemo vidjeti da su korisnici korisnički računi koje smo stvarali za izvršavanje SQL injekcija, te da jest zadnji korisnik, korisnik s korisničkim imenom admin.
Kako bi dobili njegovu lozinku, odnosno „revers“ hasha njegove lozinke, možemo iskoristiti crackstation.net, veliku bazu podataka s parovima tekstualnih vrijednosti i njihovih već izračunatih hash vrijednosti u raznim algoritmima. Unosom administratovog hash-a lozinke u crackstation, dobivamo informaciju, da se ta hash vrijednost već nalazi u crackstation bazi i da je to hash vrijednost koja se dobiva hashiranjem vrijednosti zlocinikazna algoritmom sha256.
Sada prijavom u sustav s korisničkim imenom admin i lozinkom zlocinikazna, dobivamo flag koji je rješenje zadatka.







