-15% na nową książkę sekuraka: Wprowadzenie do bezpieczeństwa IT. Przy zamówieniu podaj kod: 10000

Clickjacking i dwie kropki – opis błędu w Facebooku

23 sierpnia 2014, 18:33 | Teksty | komentarzy 10
W tym artykule opiszę znaleziony niedawno w Facebooku błąd, który pozwalał na wykradnięcie danych o użytkowniku (adres e-mail, imię, nazwisko oraz access token dający dostęp m.in. do listy znajomych).

Jedną z wielu funkcji udostępnianych przez Facebooka, obok takich jak pisanie nowych postów, czatowanie ze znajomymi czy wrzucanie nowych zdjęć, jest możliwość wykorzystania logowania na konto facebookowe z użyciem Registration Plugin. Rozwiązanie takie ma być wygodniejsze dla użytkowników, którzy nie muszą wpisywać swoich danych dziesiątki razy na różnych stronach, ale pozwolą Facebookowi udostępnić ich publiczne dane innym stronom. Przyjrzyjmy się jak wygląda proces rejestracji do jednej ze stron.

Registration Plugin

Poniższy link umożliwia zarejestrowanie się danymi z Facebooka w aplikacji FriendFeed:

https://www.facebook.com/plugins/registration.php?client_id=2795223269&fields=name,email&redirect_uri=https://friendfeed.com

Registration Plugin

Registration Plugin

Po kliknięciu „Zarejestruj się”, generowane jest zapytanie POST do adresu wskazanego w parametrze redirect_uri, zawierające widoczne na ekranie dane publiczne o użytkowniku oraz access_token, który, z użyciem facebookowego API, umożliwi aplikacji dostęp do listy znajomych.

Jak podaje Facebook w jednym z postów na blogu o tym pluginie, założenie jest takie, że plugin będzie osadzany w elemencie iframe na stronach, które będą chciały korzystać z tego typu logowania. Oznacza to, że z definicji ta strona jest podatna na Clickjacking.

Krok 1. Clickjacking

Clickjacking to atak, w którym na stronie zostaje umieszczony niewidoczny iframe, nad którym znajduje się inny widoczny obiekt – np. napis „Kliknij tutaj, aby wygrać 1000$”. W rzeczywistości, gdy użytkownik klika na ten napis, kliknięcie jest przechwytywane przez iframe będący pod spodem; wykonywana jest więc akcja z zupełnie innej podstrony niż mogło się wydawać.

Najlepiej będzie to zobrazować na przykładzie – spod adresu http://jsfiddle.net/Lg657ypz/show/. Po wejściu na stronę widzimy tylko niewinnie wyglądający przycisk „click me plox”.

Niewinny "click me plox"

Niewinny „click me plox”

Na pierwszy rzut oka trudno byłoby powiedzieć, że z tą stroną coś jest nie tak. Jednak gdy użyjemy przycisku „click!” w górnej części strony, zobaczymy, że w rzeczywistości pod „Click me plox” kryje się strona rejestrująca z Facebooka.

Jednak nie taki niewinny... ;)

Jednak nie taki niewinny… ;)

Zatem, po kliknięciu „Click me plox”, użytkownik zarejestrowałby się w pluginie friendfeed, co z całą pewnością nie byłoby oczekiwanym przez niego rezultatem.

Krok 2. Redirect_uri

Clickjacking sam w sobie nie byłby aż tak groźny w przypadku tego błędu. Zarejestrowanie się do niepożądanej strony można łatwo odkręcić, korzyści dla atakującego też nie są zbyt duże – w przedstawianym przeze mnie przypadku, użytkownik zostałby zarejestrowany do aplikacji FriendFeed, która jest i tak zarządzana przez Facebooka. Dane prywatne więc nigdzie nie wyciekają.

W przypadku rejestracji do innych stron, niezarządzanych przez Facebooka, po kliknięciu „Zarejestruj się” wyświetlane jest jeszcze dodatkowe okienko popup z pytaniem, czy na pewno chcesz się zarejestrować, co oznacza, że sam clickjacking nie wystarczyłby do przeprowadzenia ataku.

Jak wcześniej wspominałem, po zarejestrowaniu się, Facebook przekierowywał pod zasób wskazywany przez parametr redirect_uri. Co istotne, okazało się, że parametr ten nie jest sprawdzany względem białej listy (a więc nie ma stałej listy dopuszczalnych wartości), a jest w jakiś sposób walidowany. Konkretniej: dopuszczane były tylko poddomeny domeny głównej friendfeed.com. W przypadku podania domeny nienależącej do friendfeed.com, Facebook odpowiadał tylko białą stroną.

Przykład poprawnej domeny

Przykład poprawnej domeny

Przykład niepoprawnej domeny

Przykład niepoprawnej domeny

Pomyślałem, że skoro jest dopuszczalna pewna dowolność w adresach, to być może funkcja walidująca redirect_uri zawiera jakiś błąd, który będzie się dało wykorzystać do wyjścia na inną domenę. Okazało się, że intuicja mnie nie zawiodła, choć błąd jest do wykorzystania w niewielkiej części systemów.

Próbowałem stosować różne tricki w adresach do redirecta i okazało się, że przepuszczane są dowolne adresy, które zawierają sekwencję wielu kropek w nazwie domeny. Na przykład adres http://sekurak..pl/ został uznany za poprawny.

Dopuszczony adres z dwiema kropkami

Dopuszczony adres z dwiema kropkami

Początkowo uznałem, że niewiele mi to daje, gdyż nazwa domenowa sekurak..pl nie jest poprawna i nie powinna w ogóle przejść przez rozwiązywanie nazw. Postanowiłem jednak sprawdzić jak zachowują się różne przeglądarki i znalazłem dosyć interesującą ciekawostkę: Chrome na OSX i Linuksie traktuje każdą sekwencję wielu kropek w nazwie domeny tak, jakby była tam jedna kropka! Jeśli nie wierzycie, a używacie Chrome na jednym z tych dwóch systemów, to spróbujcie kliknąć na tego linka ;) (wprawdzie w odpowiedzi dostaniecie Bad Request, ale zapytanie http zostanie wysłane; żadna inna przeglądarka nie wyśle nawet zapytania, bo nie rozwiąże nazwy DNS).

Pozostało więc tylko przygotować Proof of Concept, w którym użytkownik zostaje przekonany do kliknięcia na rejestrację z użyciem Clickjackingu, a wychodzące zapytanie POST z jego danymi osobowymi zostaje przekierowane moją domenę bentkowski.info (albo bentkowski..info ;)). Dowód, że atak działał, na poniższym filmiku:

Podsumowanie

W artykule pokazałem, w jaki sposób można było doprowadzić do wycieku danych osobowych użytkownika Facebooka wykorzystując w sumie trzy problemy:

  • Clickjacking w formularzu rejestracyjnym,
  • Dopuszczanie niepoprawnych nazw domen w parametrze redirect_uri,
  • Traktowanie niepoprawnych nazw domen jako poprawnych przez Chrome na OSX i Linuksie.

Jest to też dosyć ciekawy przypadek, że podatności client-side warto sprawdzać na różnych konfiguracjach przeglądarek i systemów operacyjnych.

Błąd został zgłoszony w ramach facebookowego programu bug bounty i został już naprawiony.

Michał Bentkowski

Piotr S

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



Komentarze

  1. ​​​​​​​​​​​​​

    Ładnie opisane.

    Między innymi z uwagi na podejście facebooka do „zagrożeń” związanych z clickjackingiem używam disconnecta i każdemu polecam.

    Odpowiedz
  2. aahahah

    No third-party cookies i po kłopocie.

    Odpowiedz
  3. Lukasz

    Czyli problemem bylo nieustawione na stronie x-frame-options?

    Ile $ wpadło?

    Odpowiedz
    • X-frame-options na pewno pozostanie nieustawione, bo, zgodnie z tym co pisałem, tamta podstrona w założeniach ma być umieszczana w iframe w innych domenach. Dlatego x-frame-options zabiłoby jej sens.

      A problemem było niepoprawne walidowanie redirect_uri i to specyficzne zachowanie Chrome’a.

      Odpowiedz
  4. Dzięki, ciekawe i przystępnie opisane. Zastanawiam się: skoro działało na Linuksie i OSX, to co z Chrome na Androidzie? Biorąc pod uwagę niszowość Linuksa i OSX miał to być szanse największy wektor ataku.

    Odpowiedz
    • Fakt, nie pomyślałem wcześniej o mobilnych. Ale sprawdziłem na androidowym Chromie i nie działało tam. Być może będzie to jeszcze zależne od wersji Chrome’a/Androida.

      Odpowiedz
  5. xDDDD

    atak nie będzie działał jeśli ktoś nie ma konta na fb ;] ;] ;] ;] ;]

    Odpowiedz
  6. Bart

    Kto jest autorem odkrycia ? Michal czy xorb?

    Odpowiedz

Odpowiedz na Lukasz