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
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>:
Po pomyślnym ustawieniu etykiety na górze strony wyświetlany jest odpowiedni komunikat.
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:
Po zdekodowaniu, ciastko ma formę: tl1><test>>. Oczywiście fakt, że w ciastku pojawiają się encje HTML-owe (< oraz >) 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)>:
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):
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:
Oczywiście próbowałem zdekodować wartości obu parametrów:
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”:
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!
Łą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=&cid=5&soc-app=115&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.
Piękne połączenie :) Dobra robota!