Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

Czym jest podatność CSRF (Cross-Site Request Forgery)?

09 kwietnia 2015, 16:11 | Teksty | komentarzy 18

Po przeczytaniu tekstu będziesz wiedział:

  • czym jest podatność CSRF,
  • jak wyglądają przykładowe scenariusze ataku,
  • jak CSRF wykorzystywany jest jednocześnie z innymi podatnościami,
  • jak się chronić.

W naszej bazie tekstów mamy już opisy kilku podatności występujących w aplikacjach webowych: XSS,  SQL injection, XPATH injection, PHP Object Injection, RCE poprzez deserializację obiektów w Pythonie czy XXE. Teraz czas na kolejny problem: CSRF.

Wstęp

CSRF (Cross-Site Request Forgery; alternatwnie używane nazwy: XSRF, session riding czy one-click attack) to chyba jedna z najmniej rozumianych podatności opisywanych w ramach słynnego projektu OWASP Top Ten. Często mylona jest z podatnością XSS, czasem również prezentowana jednocześnie z innymi podatnościami, co też zaciemnia obraz istoty problemu.

Na czym więc polega istota tej podatności? Definicja OWASP mówi tak:

A CSRF attack forces a logged-on victim’s browser to send a forged HTTP request, including the victim’s session cookie and any other automatically included authentication information, to a vulnerable web application. This allows the attacker to force the victim’s browser to generate requests the vulnerable application thinks are legitimate requests from the victim.

Czyli w wolnym tłumaczeniu: jest to zmuszenie przeglądarki ofiary do wykonania pewnej nieautoryzowanej akcji (wykonania requestu HTTP). Zauważmy przy okazji, że jest to atak na przeglądarkę internetową (a nie część serwerową aplikacji webowej; dla serwera requesty powstałe w wyniku ataku to w pełni legalna komunikacja z przeglądarki użytkownika).

CSRF nie należy mylić z atakiem Man-in-The-Browser, gdzie atakujący też może wpływać na działanie przeglądarki, ale wiąże się to z wcześniejszym zainstalowaniem w systemie ofiary malware. W przypadku CSRF, system jak i przeglądarka ofiary nie są w żaden sposób trwale modyfikowane. Wykorzystana jest po prostu pewna właściwość architektury web i przeglądarek internetowych.

CSRF to również nie to samo co XSS. Jeśli mamy XSS-a – to da się zrealizować CSRF ale jeśli nasza aplikacja podatna jest na CSRF – to niekoniecznie musimy być podatni na XSS. Z kolei sam Cross-Site Scripting  może służyć do ominięcia metod ochrony przed CSRF (szczegóły w dalszej części tekstu).

Chyba najlepiej uczyć się na konkretnych przykładach, zobaczmy więc od razu pierwszy scenariusz wykorzystania podatności CSRF, zmuszający przeglądarkę administratora aplikacji webowej do wykonania requestu http, który doda w niej nowego użytkownika.

Przykład 1. – panel administracyjny forum.

W tym przypadku dostępną mamy aplikację (forum dyskusyjne) pod jedną domeną. Panel administracyjny posiada ograniczony dostęp (logowanie tylko z określonej puli adresów IP). Atakujący będzie chciał zmusić przeglądarkę administratora (wykorzystać podatność CSRF) do zarejestrowania nowego konta.

CSRF – przykład

Całość odbywa się w kilku krokach:

  1. Atakujący rejestruje konto, podając w swoim loginie tag: <img src=”http://pro-forum.sekurak/admin/addUser?login=malgorzata4&pass=1234&type=adm”>
    Możliwe są tu również inne sposoby ataku (tag <img> to tylko przykład).
  2. Administrator loguje się oraz wchodzi na stronę z akceptowaniem nowych kont.
  3. Podczas próby pobrania obrazu z tagu <img>, przeglądarka administratora realizuje automatycznie request do panelu administracyjnego (mamy tu CSRF) – i tym samym tworzy nowe konto w systemie o uprawnieniach administracyjnym.
  4. Aby atakujący mógł się zalogować wystarczyłoby jeszcze raz wykorzystać CSRF do wykonania np. requestu usuwającego blokadę na IP użytkownika.

Wnioski:

  1. Atak zadziałał mimo tego że ofiara początkowo nawet nie mogła dostać się do panelu administracyjnego (ograniczenie na adres IP).
  2. W ataku nie został wykorzystany javascript.
  3. W ataku nie została wykorzystana podatność XSS (choć gdyby ona była dostępna w aplikacji – można by również zrealizować CSRF – wykonując odpowiedni request za pomocą javascript).
  4. Atakujący nie znał loginu ani hasła administratora.
  5. W logach serwera www jako źródłowy adres IP requestu, który dodał nieautoryzowanego użytkownika widoczny będzie realny adres IP atakowanego administratora (nie będzie to więc adres IP atakującego).
  6. Atakujący nie widzi odpowiedzi na request do którego wykonania został zmuszony administrator, ale nie ma to wpływu na skuteczność ataku.

W takim scenariuszu może być atakowany w zasadzie dowolny panel administracyjny aplikacji webowej, który przetwarza (np. wyświetla) pewne dane kontrolowane od użytkownika.

Z przykładu widać, że domyślnie aplikacje webowe są na CSRF podatne (chyba ze użyły osobnych bibliotek czy mechanizmów ochronnych) – po prostu w ten sposób została zaprojektowana architektura web.

W tym momencie może też powstać pytanie: co byłoby w przypadku gdyby aplikacja odpowiednio filtrowała wartości przekazywane przez użytkownika do pola login? CSRF również byłby możliwy – ale ścieżka ataku wyglądałaby nieco inaczej. Zobaczmy więc kolejny przykład.

Przykład 2. CSRF i router dostępowy do Internetu.

Tym razem prześledźmy scenariusz wykorzystujący dwie domeny (pod jedną jest atakowany system, drugi to domena pomocnicza z wprowadzonymi przez atakującego pewnymi zmianami). Dodatkowo, scenariusz ten wykorzystuje poza CSRF jeszcze dodatkowe podatności w aplikacji webowej.

Podatności w routerze ASMAX

Jakiś czas temu udało mi się znaleźć kilka błędów bezpieczeństwa w routerze ASMAX (podatności te swoją drogą nie zostały chyba do dzisiaj załatane). Najistotniejszą z nich była możliwość wykonania kodu w systemie operacyjnym routera odwołując się bez uwierzytelnienia do zasobu /cgi-bin/script – na przykład w ten sposób:

OS Command Exec – ASMAX router

Mieliśmy więc tutaj dwie podatności: 1. wstrzyknięcie kodu systemu operacyjnego, 2. ominięcie uwierzytelnienia (a de facto jego brak).

Podatności w routerze ASMAX – CSRF

Jak ma się do tego omawiany CSRF? Zobaczmy przykładowy scenariusz ataku na urządzenie, przy dodatkowym założeniu, że panel zarządczy urządzenia w ogóle nie jest dostępny z poziomu Internetu. Atak ten skutkował będzie nieograniczonym dostępem (jako root) na systemie operacyjnym urządzenia.

Przykład CSRF – ASMAX router

Całość ataku odbywa się w kilku krokach (cyfry 1-4 na zrzucie ekranowym powyżej):

  1. Atakujący umieszcza na dowolnej stronie webowej tag <img> z parametrem src kierującym do sieci lokalnej, a dokładnie na: „http://192.168.1.1/cgi-bin/script?system reboot”.
  2. Ofiara wchodzi na ww. stronę (musi zostać do tego nakłoniona; to częsty wymóg CSRF) – strona HTML wraz z obrazkiem ściąga się na komputer ofiary (aby zostać w kolejnym kroku wyrenderowana).
  3. Podczas próby pobrania obrazka przeglądarka wykonuje request do routera (tutaj przeglądarka została zmuszona do wykonania nieautoryzowanej akcji w obrębie LAN) – CSRF!
  4. Router rebootuje się (lub wykonuje dowolne polecenie zaszyte w punkcie 1. obrazka).

Zauważmy przy okazji:

  1. Aby wykonać skuteczny atak, ofiara nie musiała być zalogowana do routera (wykorzystany został tutaj fakt, że dostęp do /cgi-bin/script nie wymaga uwierzytelnienia).
  2. W ataku nie został wykorzystany javascript.
  3. W ataku nie została wykorzystana podatność XSS.
  4. Atakujący nie znał loginu ani hasła administratora routera.
  5. Atak się powiódł mimo niedostępności panelu administracyjnego urządzenia z poziomu Internetu.
  6. Atak wymagał nakłonienia ofiary do odwiedzenia innej strony.
  7. Zamiast wykonania polecenia reboot – mogłoby zostać wykonane dowolne inne polecenie w OS routera – np. dociągnięcie zewnętrznego pliku binarnego (backdoora) czy odblokowanie dostępu do panelu administracyjnego.

CSRF na urządzeniu sieciowym – alternatywny scenariusz

Co ciekawe, gdyby w konsoli webowej routera nie udało się znaleźć podatności umożliwiającej nieuwierzytelnione requesty, można spróbować użyć np.: takiego obrazka <img src=”http://admin:admin@192.168.1.1/aaa>

Dla dociekliwych – w źródle HTML artykułu umieściłem taki obrazek i przeglądarki zazwyczaj wykonują request w nim zawarty (wysyłając też też login i hasło – przy założeniu że mamy pod takim adresem mechanizm Basic Access Authentication). Co ciekawe, np. Firefox w przypadku wpisania do paska URL tego adresu (http://admin:admin@192.168.1.1/aaa), wyświetla komunikat:

csrf8Ale gdy ten sam URL zawarty jest w tagu IMG – komunikatu już nie ma (a request jest wysyłany). Warto zauważyć że ta metoda mogłaby też służyć do bruteforce-u hasła dostępowego do router-a (poprzez generowanie dużej liczby tagów IMG).

Przykład 3. – bankowość elektroniczna.

Zobaczmy inny często przytaczany przykład wykorzystania CSRF – tym razem wymagający uwierzytelnienia:

csrf-banking

CSRF – bankowość elektroniczna

W tym przypadku:

  1. Atakujący umieszcza na stronie eeeevil-zite.com tag <img> realizujący request odpowiadający realizacji przelewu w bankowości elektronicznej – na swoje konto. Równie dobrze mógłby to być również samoczynnie wysyłający się formularz typu POST.
  2. Ofiara loguje się do bankowości elektronicznej.
  3. Ofiara wchodzi w innej zakładce przeglądarki na eeeevil-zite.com
  4. Ofiara poprzez punkt 3. realizuje nieświadomie request (przelew) do swojej zalogowanej sesji w bankowości elektronicznej.

Oczywiście większość systemów bankowości elektronicznej jest w obecnie chroniona zarówno przed samą podatnością CSRF jak i dodatkowo wymaga dodatkowej autoryzacji przy przelewie na nieznane konto – przynajmniej tyle mówi teoria ;-)

Zauważmy również, że gdyby bankowośc przyjmowała requesty HTTP tylko metodą POST –  na eeeevil-zite.com moglibyśmy po prostu użyć odpowiednio spreparowany i samoczynnie wysyłający się formularz typu POST. Zatem korzystanie tylko requestów typu POST nie chroni przed CSRF. W tym przypadku OWASP podaje taki prosty przykład:

<body onload="document.forms[0].submit()">
<form action="http://bank.com/transfer.do" method="POST">
<input type="hidden" name="acct" value="MARIA"/>
<input type="hidden" name="amount" value="100000"/>
<input type="submit" value="View my pictures"/>
</form>

Zobaczmy zatem jakie mamy możliwe metody ochrony.

Metody ochrony przed CSRF.

Ochronę możemy tutaj zrealizować na kilka alternatywnych sposobów. Jednocześnie warto pamiętać, że najistotniejsza jest ochrona funkcjonalności (requestów) realizujących zdarzenia zmieniające pewne wartości w systemie (np. realizujące przelew, zmieniające hasło, itp.) – atakujący i tak nie widzi bezpośrednio wyniku wykonania akcji (widzi to ofiara) – więc np. CSRF na listowaniu informacji o przelewach zazwyczaj niewiele daje.

Z drugiej strony – wdrożenie ochrony na całym systemie ułatwia uniknięcie braku ochrony np. dla nowych funkcjonalności (bo implementujący ją zapomniał o problemie). Przejdźmy do konkretów.

Losowe tokeny

Pierwszą zalecaną przez OWASP metodą jest wzorzec: Synchronizer Token Pattern, czyli użycie losowych tokenów (ciągów znaków) związanych z zalogowaną sesją (session tokens). Podczas tworzenia zalogowanej sesji użytkownika generowany jest ciąg znaków, który przekazywany jest do kolejnych requestów np. w taki sposób:

<form action="/transfer.do" method="post">
  <input type="hidden" name="CSRFToken" 
  value="OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWE...
  wYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZ...
  MGYwMGEwOA==">
  …
  </form>

Strona obsługująca formularz musi z kolei sprawdzić czy przekazany token to rzeczywiście jest wartość, która została wygenerowana przez aplikację.

Atakujący oczywiście nie zna tokena, więc nie może przygotować akceptowalnych przez aplikację requestów GET („pakowanych” np. w tag IMG) czy POST (umieszczonych na innej domenie jako automatycznie wysyłający się formularz).

Zauważmy też, że wyciek takiego tokena powoduje możliwość ominięcia ochrony przed CSRF. Przykładowym scenariuszem wykradającym od ofiary token może być wykorzystanie podatności XSS. W takim przypadku strona realizująca CSRF implementuje dwa kroki:

  1. Pobranie tokenu z wykorzystaniem XSS (użycie XMLHttpRequest i odczytanie z odpowiedzi tokenu)
  2. Wygenerowanie requestu z osadzonym już tokenem.

Zatem jeśli mamy w naszej aplikacji XSS, najprawdopodobniej będzie się dało ominąć ochronę przez CSRF.

Jako zwiększenie poziomu ochrony, OWASP sugeruje również tworzenie tokenów nie per sesję ale per request (tzw. page tokeny). Czyli po wysłaniu formularza stary token traci ważność, a na stronie wynikowej generowane są nowe tokeny (dla miejsc, które można osiągnąć ze strony po wysłaniu formularza). Takie działanie może mieć z kolei pewne nieoczekiwane efekty jeśli chodzi o funkcjonalność aplikacji (np. nie będzie działał przycisk „wstecz” – użyte na poprzedniej stronie tokeny już zostały unieważnione…).

Idealnie byłoby również akceptować wrażliwe akcje od użytkownika tylko metodą POST (wtedy nawet nie ma potrzeby zawierania tokenów w requestach typu GET; pamiętajmy że parametry requestów GET często domyślnie zapisywane są w logach serwerów WWW czy wysyłane w nagłówku HTTP Referer – potencjalnie mogą więc one w niekontrolowany sposób wyciekać).

W określonych sytuacjach wygodne może być również wysyłanie tokenów w Cookies (ale całość musi bazować na opisanym wcześniej schemacie: Synchronizer Token Pattern).

Przypominam raz jeszcze, że samo generowanie tokenów antiCSRF nie rozwiązuje problemu – należy również koniecznie sprawdzać ich poprawność. Niektórzy twórcy CMS-ów na zgłoszenie podatności CSRF reagują np. tak:

Tokeny antiCSRF mamy, ale jakoś tak wyszło że zapomnieliśmy sprawdzać ich poprawność po stronie serwerowej

Zresztą zdarza się to i gigantom, np. Facebook-owi (za znalezienie tego CSRF wypłacono $5000 w ramach programu bug bounty).

Double Submit Cookies

Ta metoda polega na wysyłaniu przez przeglądarkę użytkownika ten samej – losowo wygenerowanej przez aplikację   wartości – za pomocą: requestu HTTP oraz ciasteczka. Serwer sprawdza czy wartość zapisana w ciastku i wysłana danym requeście http jest taka sama. Jeśli nie – zgłasza próbę wykorzystania CSRF. Zaletą tej metody jest to, że nie ma potrzeby zapisywania po stronie serwera wcześniej wygenerowanej wcześniej wartości.

Użycie gotowych bibliotek

Jeśli biblioteka / framework z którego korzystamy ma możliwość ochrony przez CSRF – użyjmy jej!

Przykład to choćby: django, spring, biblioteka OWASP ESAPI czy różne sposoby radzenia sobie w ASP.NET.

Podsumowanie

CSRF nie jest zazwyczaj krytyczną podatnością, choć wskazanie jej na liście OWASP Top Ten zdecydowanie nie pozwala ignorować problemu. Zazwyczaj też do wykorzystania luki potrzebna jest dodatkowa akcja ze strony ofiary (np. odwiedziny odpowiednio spreparowanej strony), co też zmniejsza (jednak oczywiście nie do zera) prawdopodobieństwo skutecznego ataku. Pamiętajmy też, że bez użycia dodatkowych środków ostrożności, większość aplikacji webowych jest podatna na CSRF.

 

Michał Sajdak, realizuje testy penetracyjne,
prowadzi szkolenia z bezpieczeństwa IT w Securitum.

 

 

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. tonieja

    Przykład, jak CSRF prowadzić może do RCE na przykładzie osCommerce (na dole):
    http://1337day.com/exploit/22424

    Z innej beczki, apropo wyjmowania tokenów CSRF via XSS. PoC na RCE (file upload) przez XSS w wordpressie:
    http://pastebin.com/raw.php?i=VtFgn1TX

    Niedawno testowane na najnowszej wersji, powinno działać, nie jest to bug w aplikacji zaś wykorzystanie funkcji cms’a i js.

    Odpowiedz
  2. darek

    Fajne podsumowanie CSRF. Dzięki. Dodam, że spotkałem się też z pojęciem OSRF (on site request forgery). Jeśli dobrze rozumiem koncepcję to pierwszy przykład można właśnie podpiąć pod OSRF.

    Odpowiedz
    • No ciekawe rozróżnienie z tym OSRF. Ale chyba jednak bardzo niszowe? Na samym OWASP-ie chyba nic o tym nie ma?

      Odpowiedz
      • darek

        Rzeczywiście raczej niszowe rozróżnienie. W zasadzie to specyficzny sposób wykorzystania CSRF, o którym warto pamiętać kiedy celujemy w Stored XSS i nie wychodzi. Z tego co pamiętam o OSRF czytałem w „Web Application Hacker’s Handbook”

        Odpowiedz
  3. marcin
    Odpowiedz
  4. Paweł

    Jak się ma sprawa w przypadku ciasteczek http-only?
    Wtedy raczej CSRF nie jest możliwe, bo przeglądarka zablokuje XHR na inną domenę, a przez czy nie wyśle ciasteczka z identyfikatorem sesji.

    Odpowiedz
    • Ale ten XHR był potrzebny nie do CSRF tylko do omijania blokady.
      W normalnym CSRF to nie trzeba w ogóle mieć dostępu do cookiesów.

      Odpowiedz
      • Paweł

        Miałem na myśli to, że jeśli serwis atakowany ustawia ciasteczka sesji jako http-only, to atak CSRF się nie powiedzie, bo te nie zostaną wysłane (user żąda obrazka, przeglądarka ich nie wysyła).
        Ale wtedy można wykorzystać trick z POSTem.

        Odpowiedz
        • Powiedzie, się powiedzie. Bo zrobienie CSRF nie wymaga w ogóle użycia javascriptu. A http-only chroni ciastko przed dostępem do javascriptu.

          Odpowiedz
  5. Marek

    Czy sprawdzanie nagłówka Refer samo w sobie może być ochroną przed CSRF?

    Odpowiedz
    • Może być – ale można to obejść :)

      Odpowiedz
  6. Michu

    „W ataku nie została wykorzystana podatność XSS (choć gdyby ona była dostępna w aplikacji – można by również zrealizować CSRF – wykonując odpowiedni request za pomocą javascript).”

    To nieprawda – w Waszym przykładzie to właśnie XSS spowodował CSRF. XSS to nie tylko osadzenie , ale również i dowolnego innego tagu, który cokolwiek sam wykona.

    Odpowiedz
    • Michu

      I taka rada – nie usuwajcie tagów z komentarzy, tylko je wyświetlajcie interpretując jak tekst. Chciałem wstawić w komentarzu [script] i [img], ale zniknęły. Niezbyt to rozsądne.

      Odpowiedz
    • Michu: a gdzie tu jakieś wstrzyknięcie skryptu (JS/VBScript, itp) w ataku?

      Odpowiedz
      • Michu

        Nie ma wstrzyknięcia skryptu, bo nie musi być. Wiem, że niektórzy traktują XSS dosłownie, „bo w nazwie jest script”. Ale XSS to jest wstrzyknięcie czegokolwiek aktywnego w treść HTML-a naszej strony. A tag obrazka to jest coś aktywnego, bo wysyła żądanie bez żadnej interakcji ze strony odwiedzającego.

        Odpowiedz
        • Michu2

          w takim razie czym jest HTML Injection :)?

          Odpowiedz
        • Bichu

          @Michu nadal tak uważasz?

          Odpowiedz

Odpowiedz