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

Gmail i Google+ – czyli opowieść o dwóch XSS-ach

30 kwietnia 2014, 11:03 | Teksty | 1 komentarz
W tym artykule opiszę dwa błędy typu XSS znalezione w aplikacjach Google’a. W szczególności pokażę, że szukanie XSS-ów w ciasteczkach http jest uzasadnione oraz podam przykład, jak w praktyce można tego typu błąd wykorzystać.

 

XSS w Gmailu

Na początek weźmy na tapetę jedną z najpopularniejszych usług Google’a, jaką jest Gmail. Gmaila można przeglądać w wielu wariantach: poza najpopularniejszą wersją przewidzianą na przeglądarki wspierające HTML5, udostępniane są także wersje dla telefonów komórkowych (nowszej i starszej generacji) oraz wersja basic HTML. XSS, o którym będę pisał, występował zarówno w wersji basic HTML, jak i w tej dla starszych komórek.

Możliwości obu wersji stanowią tylko pewien podzbiór wszystkich funkcji Gmaila, ale można w nich wykonać wszystkie podstawowe operacje, zarówno takie jak czytanie i odbieranie maili, jak i, a może przede wszystkim, ustawianie etykiet.

Dla potrzeb przykładu, próbuję ustawić etykietę <test>:

gmail-image1

Po pomyślnym ustawieniu etykiety na górze strony wyświetlany jest odpowiedni komunikat.

gmail-image2

Przeglądając komunikację http serwera z przeglądarką, zauważyłem, że nazwa etykiety wyświetlana w komunikacie jest przechowywana w ciasteczku GMAIL_NOTI:

gmail-image3

Po zdekodowaniu, ciastko ma formę: tl1>&lt;test&gt;>. Oczywiście fakt, że w ciastku pojawiają się encje HTML-owe (&lt; oraz &gt;) natychmiast zwracał uwagę. Może gdyby, zamiast encji, użyć nawiasów ostrych (< i >), udałoby się wrzucić dowolny tag na stronę?

Spróbowałem więc z jednym z najpopularniejszych payloadów XSS-owych: <img src=1 onerror=alert(1)>:

gmail-image4

Niestety serwer odpowiedział kodem 500… Całe szczęście, obejście problemu okazało się dosyć proste. Wystarczyło pominąć zamykający nawias ostry na końcu wartości ciastka, a więc zostawić samo: <img src=1 onerror=alert(1):

gmail-image5

gmail-image6

Cel osiągnięty: mamy XSS-a w domenie mail.google.com.

Problemem jest jednak próba wykorzystania tego XSS-a w praktyce; niezbędna do tego jest możliwość ustawienia dowolnych ciastek w przeglądarce.

W jaki sposób można to zrobić?

  • Wykorzystując podatność response splitting, dodać nagłówek Set-Cookie.
  • Wykorzystując brak filtrowania w istniejącym nagłówku Set-Cookie. Przykład takiej podatności.
  • Wykorzystując innego XSS-a, przypisać odpowiednią wartość do document.cookie.

Pierwsze dwie podatności dość rzadko występują w aplikacjach webowych, skupmy się więc na trzeciej: wykorzystamy innego XSS-a do wyexploitowania tego XSS-a. Z początku może to brzmieć trochę absurdalnie, ale takie rozwiązanie ma sens. Żeby to wyjaśnić, przypomnijmy sobie, jakie atrybuty dla ciastek mogą być ustawiane w nagłówku Set-Cookie:

  • Expires,
  • Max-Age,
  • Domain,
  • Path,
  • Secure,
  • HttpOnly.

Wykorzystamy atrybut Domain, który pozwala zdefiniować, dla jakich domen ciasteczko będzie wysyłane. Wyobraźmy sobie, że mamy domenę test.google.com, na której ustawiane byłoby ciastko:

Set-Cookie: Test=test; Domain=.google.com

Zapis .google.com sprawia, że to ciasteczko będzie wysyłane do wszystkich subdomen w ramach domeny google.com. Zapytanie http będzie więc zawierało to ciastko niezależnie od tego, czy użytkownik przejdzie pod test.google.com, mail.google.com, plus.google.com czy accounts.google.com.

Zatem, aby wykorzystać XSS-a w mail.google.com, musimy znaleźć innego XSS-a w dowolnej innej subdomenie Google’a. Biorąc pod uwagę ogrom istniejących subdomen i usług, nie powinno to być aż tak trudne.

 

XSS w Google+

Poszukiwania kolejnej podatności przywiodły mnie do serwisu społecznościowego Google+. Podobnie jak w przypadku Gmaila, nie zaglądałem na główną wersję strony, ale na jej wersję mobilną.

Tym razem podatna jest funkcja uploadu obrazków. Zauważyłem, że w zapytaniu http zawierającym wrzucany obrazek znajdują się bardzo długie parametry o nazwie puSuccessResponse oraz puFailureResponse zakodowane w Base64:

gmail-image7

Oczywiście próbowałem zdekodować wartości obu parametrów:

gmail-image8

Okazało się, że zawierają one kod html stron, które zostaną wyświetlone po uploadzie obrazka! Jeśli upload się uda, w odpowiedzi wyświetlany jest puSuccessResponse, w przeciwnym wypadku puFailureResponse. Teoretycznie wartość tych parametrów mogła być jakoś walidowana i odrzucana, jeśli nie pasuje do wzorców. Spróbowałem więc wyświetlić HTML-a o treści „Witaj”:

gmail-image9

Wyglądało na to, że jednak żadnej walidacji tam nie ma… Wyglądało na to, że trzeba się będzie zmierzyć z jeszcze jednym problemem: na obrazku powyżej widać, że w zapytaniu http pojawia się parametr at, który jest tokenem anty-CSRF. Okazało się jednak, że jeśli całkiem usunę ten parametr z zapytania, to serwer odpowie kodem 500…, ale i tak wyświetli w odpowiedzi puFailureResponse!

gmail-image10

 

Łączymy wszystko w jedną całość

Zatem mam XSS-a w ciasteczkach http w Gmailu oraz XSS-a w parametrze POST w Google Plus. Pozostaje tylko stworzyć jeden kod, który wyexploituje oba XSS-y.

Oto gotowy kod:

<html>
  <body>
    <form action="https://plus.google.com/_/upload/app/basic/photos?cbp=&amp;cid=5&amp;soc-app=115&amp;soc-platform=1" method="POST" enctype="multipart/form-data">
      <input type="hidden" name="puSuccessResponse" value="aGVq" />
      <input type="hidden" name="puFailureResponse" value="PHNjcmlwdD4KYWxlcnQoIkR1ZGUsIHlvdSdyZSBYU1MtZWQgb24gIitkb2N1bWVudC5kb21haW4pOwpkb2N1bWVudC5jb29raWU9IkdNQUlMX05PVEk9dGwxPjxpbWcrc3JjJTNkMStvbmVycm9yJTNkJTIyYWxlcnQoJ0FuZCtub3crb24rJyUyYmRvY3VtZW50LmRvbWFpbiklMjIreDsgIERvbWFpbj0uZ29vZ2xlLmNvbTsgUGF0aD0vIjsKZG9jdW1lbnQubG9jYXRpb24gPSAiaHR0cHM6Ly9tYWlsLmdvb2dsZS5jb20vbWFpbC94LyI7ICAgCjwvc2NyaXB0PiAg" />
      <input type="submit" value="Double XSS!" />
    </form>
  </body>
</html>

Powyższy kod spowoduje wykonanie XSS-a w Google Plus po kliknięciu na przycisk „Double XSS!”. Parametr puFailureResponse dekoduje się do:

<script>
alert("Dude, you're XSS-ed on "+document.domain);
document.cookie="GMAIL_NOTI=tl1><img+src%3d1+onerror%3d%22alert('And+now+on+'%2bdocument.domain)%22+x;  Domain=.google.com; Path=/";
document.location = "https://mail.google.com/mail/x/";   
</script>

Zostanie więc wyświetlony alert o treści „Dude, you’re XSS-ed on plus.google.com”. Następnie ustawione zostanie ciastko GMAIL_NOTI o wartości:

tl1><img+src=1+onerror="alert('And+now+on+'+document.domain)"+x.

Dzięki temu, po przejściu na domenę mail.google.com zostanie wyświetlony alert o treści „And now on mail.google.com”. Na końcu użytkownik zostanie automatycznie przekierowany do podatnej podstrony Gmaila.

Dowód, że exploit zadziałał, możecie zobaczyć na filmiku:

 

Podsumowanie

Poszukiwanie XSS-ów w ciasteczkach http jest równie uzasadnione jak w parametrach GET czy POST. Szczególnie narażone są firmy, które korzystają z wielu subdomen do hostowania swoich stron, wówczas XSS z jednej domeny może być eskalowany do innej (potencjalnie o wyższej istotności).

Oba błędy zostały zgłoszone w ramach programu Google Vulnerability Reward. Zachęcam do spróbowania swoich sił w tym lub innych programach bug bounty, gdyż dają świetną możliwość rozwinięcia swoich umiejętności.

 

Michał Bentkowski

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



Komentarze

  1. darek

    Piękne połączenie :) Dobra robota!

    Odpowiedz

Odpowiedz