Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Pozwalasz ładować pliki SVG? Masz XSS-a!
Dodawanie plików przez użytkowników web aplikacji wiąże się z wieloma zagrożeniami. Pentesterzy w tym obszarze często szukają luk prowadzących do zdalnego wykonania kodu po stronie serwera. A co gdyby dodanie nowego pliku skutkowało wykonaniem złośliwego skryptu JS?
Taką możliwość dają pliki SVG opisujące grafikę wektorową we współczesnych przeglądarkach.
Grafika czy dokument?
Intuicyjnie Scalable Vector Graphic jest grafiką wektorową, która definiuje obraz kształtami, zamiast kolorami poszczególnych grup pikseli. Dzięki temu obrazki SVG są nieskończenie skalowalne: zarówno miniatura jak i obraz przedstawiony na wielkim ekranie będzie wyglądał tak samo dobrze – bez straty jakości kojarzącej się z grafiką rastrową reprezentowaną w internecie przez pliki takie jak gif, png czy jpeg.
Format SVG natywnie wspierany jest we wszystkich przeglądarkach od wielu lat, wliczając w to Internet Explorer 9+ oraz przeglądarki mobilne. W związku z tym popularność SVG ciągle rośnie i format ten obecnie staje się standardem dla różnej maści ikon i bootstrapów interfejsów współczesnych stron internetowych.
Wróćmy jednak do kontekstu bezpieczeństwa. Określenie SVG jako grafiki jest dużym skrótem myślowym. W praktyce SVG nie jest formatem graficznym, a dokumentem XML opisującym elementy składające się na grafikę oraz jej dodatkowe interakcje z otoczeniem. Tak jest – pliki SVG mogą być interaktywnym dokumentem – takim jak HTML – i zmieniać się w zależności od zaprogramowanych wcześniej akcji.
Tak jak w HTML mamy drzewo DOM, tak i SVG ma swoje drzewo obiektów (SVG DOM), które jest dostępne z poziomu skryptów JS. W związku z tym, że HTML, Javascript oraz SVG działają w tym samym ekosystemie – czyli w przeglądarce – oznacza to, że bez problemu można stworzyć plik SVG wykonujące złośliwe akcje w domenie testowanej aplikacji internetowej.
SVG = XSS
Załóżmy, że pewien serwis posiada funkcję uploadu plików. Dla zwiększenia bezpieczeństwa twórca serwisu decyduje się wyłącznie na możliwość ładowania plików graficznych, w tym plików SVG.
Co się stanie, gdy agresor załaduje poniższy plik SVG do serwisu?
<svg xmlns="http://www.w3.org/1999/svg"> <script> alert(1) </script> </svg>
Plik zostanie zapisany i dostęp do niego w omawianym przypadku będzie możliwy przez odwołanie się do tej samej domeny (na przykład przez katalog images lub skrypt wyświetlający obrazek o podanym identyfikatorze). Do przeglądarki obrazek trafi w postaci parametru src elementu <img>. Wynikiem będzie wyświetlenie wyłącznie atrybutu alt pustego obrazka:
Gdy jednak odwołamy się po pełnej ścieżki powyższego pliku graficznego zauważymy, że udało się nam zapisać plik wykonujący atak Persistent XSS:
Warto teraz zwrócić uwagę na nagłówki odpowiedzi. W powyższym przykładzie programista nie określił typu zwracanej odpowiedzi dla skryptu serwującego obrazki – po prostu (naiwnie) polegał na logice serwera lub na mechanizmie Content Sniffing przeglądarki. Plik SVG został zinterpretowany jako text/html, czyli dokładnie tak, jak zwykła strona HTML z zaszytym skryptem inline (kilka przykładów tego rodzaju wstrzyknięć opisano w drugiej części cyklu dotyczącego bezpieczeństwa HTML5 – sekcja SVG).
Jednak taka sytuacja nie jest często spotykana i nie jest szczególnie ciekawa – w takim przypadku prawdopodobnie możemy uploadować dowolny kod HTML prowadzący do luki HTML Injection, więc SVG nie dodaje tu nic szczególnego. Zdecydowanie częściej występuje sytuacja, gdzie nagłówek Content-Type zawiera wartość odpowiadającą zawartości wcześniej załadowanego pliku. W przypadku SVG poprawnym typem MIME jest image/svg+xml.
Gdy taki nagłówek zostanie zwrócony, wcześniejszy atak nie dojdzie do skutku:
Okazuje się, że powyższe zabezpieczenie można obejść.
W tym celu stworzymy poprawny plik SVG, który będzie zawierał w sobie zarówno grafikę jak i skrypt wyświetlający komunikat demonstrujący wykonanie ataku XSS:
<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"> <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/> <script type="text/javascript"> alert('This app is probably vulnerable to XSS attacks!'); </script> </svg>
Zakładamy, że programista poprawnie odsyła nagłówki Content-Type oraz dodaje rozszerzenie .svg do nazwy pliku, aby przeglądarka nie miała żadnych wątpliwości o serwowanej treści. Po załadowaniu pliku przez mechanizmy serwisu i wczytanie nowego zasobu zobaczymy, że dalej jesteśmy w stanie wykonywać złośliwe skrypty.
Poprawnie obsłużony plik SVG stał się furtką do ataku Persistent XSS:
A gdy musimy umożliwić upload SVG?
SVG jest dokumentem opisującym grafikę oraz jej dodatkowe zachowanie, dlatego też za wszelką cenę powinniśmy zabraniać możliwości ładowania tego rodzaju dokumentów przez użytkowników naszej aplikacji internetowej. Czasami jednak sytuacja projektowa wymaga dodanie takiej funkcji do serwisu – jak sobie poradzić w takiej sytuacji?
Po pierwsze należy sobie odpowiedzieć na pytanie, czy użytkownicy oprócz dodawania plików koniecznie muszą je wyświetlać. Może wystarczy, gdy dodamy funkcję ściągania SVG, zamiast ich pokazywania? W takiej sytuacji atak XSS nie będzie niczym groźnym, ponieważ złośliwy skrypt będzie odpalany z innego pochodzenia (z dysku) niż domena, z której ściągnięto plik.
Jeśli wdrożenie powyższej rady nie wchodzi w rachubę, mamy przed sobą większe wyzwanie.
Najpierw należy upewnić się, czy ustawiamy dobre nagłówki bezpieczeństwa. Podstawowymi nagłówkami odpowiedzi HTTP z plikami SVG będą:
- Content-Type: image/svg+xml,
- X-Content-Type-Options: nosniff.
Oprócz tego magazyn plików SVG warto przenieść do osobnej domeny lub subdomeny, która nie będzie wymagać uwierzytelnienia chociażby przez takie mechanizmy jak ciasteczka (do plików można odwoływać się np. po identyfikatorze GUID, warto też dodać rozszerzenie .svg do nazw plików). W takiej sytuacji udany atak XSS będzie dużo mniejszym zagrożeniem.
Następnie należy skonfigurować bardzo rygorystyczną politykę Content Security Policy (CSP) – czy to dla skryptu zwracającego treść grafiki wektorowej, czy też dla serwera, który serwuje pliki SVG z katalogu. Ustawienie CSP na wartość taką jak Content-Security-Policy: script-src 'none’ wyłączy możliwość wykonywania skryptów dla danego dokumentu (czyli w złośliwej grafice SVG).
Pliki SVG można również przetwarzać po stronie serwera i konwertować na bezpieczniejsze formaty obrazów rastrowych (np. png). Można również pokusić się o usunięcie potencjalnie niebezpiecznych skryptów – do tego celu można wykorzystać np. narzędzie SVG Purifier.
Jak widać pomysłów radzenia sobie z formatem SVG jest całkiem sporo, jednak zdecydowanie najlepszym wyjściem jest stosowanie plików SVG tylko przez programistów i ścisły zakaz uploadu tego rodzaju dokumentów.
Powiedz NIE plikom SVG z niezaufanego źródła.
–vizzdoom
Dobry materiał, tylko dlaczego taki krzykliwy tytuł? Równie dobrze można by zatytułować „Pozwalasz ładować pliki JS / XML, masz XSS-a!”.
Ale do rzeczy: moim skromnym zdaniem jest to mało serdeczny atak na SVG zamiast na domyslnie niebezpieczne zachowania przeglądarek, typu: „Nie wiem jaki to Content-*, więc pewnie text ze skryptami po stronie klienta”.
W 21-ym wieku przeglądarki powinny wspomagać end-usera w jego bezpieczeństwie, a nie programistę w jego lenistwie.
Tutuł krzykliwy, aby zapadł w pamięć. Dużo ciężej, aby pojawiło się wymaganie biznesowe, które miałoby skutkować implementacją uploadu JS/XML (tutaj raczej developer pomyśli, że coś tu może nie grać), a sam spotkałem się z sytuacjami, że można było dodawać pliki SVG na stronach, gdzie zachciało się komuś ładować nowoczesne formaty audio/video.
Pamiętajmy, że XSS w subdomenie strony również `może` być groźny i wpływać na domenę wyższego poziomu. A dla potrzeb artykułu (który ma czegoś nauczyć) przykłady się trochę upraszcza.
Emm, ale czy wyświetlanie plików SVG na stronie tylko i wyłącznie za pomocą <img /> i nonce-y we wszystkich odwołaniach nie stanowią wystarczającego zabezpieczenia?
Gdy plik jest hostowany na tej samej domenie (lub subdomenie) co testowana strona, to wtedy istnieje niebezpieczeństwo. Odwołanie się do tego pliku bezpośrednio w przeglądarce może spowodować wykonanie złośliwych skryptów.
Przeglądarki wyłączają obsługę javascript w plikach SVG w znaczniku img. Jeśli więc wyłącznie wyświetlamy SVG w img src, wtedy wykonanie złośliwych skryptów nam nie grozi.
„SVG nie jest formatem graficznym, a dokumentem XML” – SVG jest to format grafiki z paroma dodatkami. XML to tylko sposób zapisu informacji graficznych. Tak samo jak DOC to format dokumentu tekstowego, chociaż może zawierać makro czy grafikę, a XLSX to nie dokument XML, tylko arkusz kalkulacyjny zapisany w standardzie XML, który też przez swoje dodatkowe możliwości może więcej, niż tylko przechowywać dane. JPEG to nie format graficzny, a plik binarny. W plikach WMV też można zawrzeć niebezpieczne dane, a mimo to w teorii i w praktyce jest to format wideo. Nie rozumiem więc co odbiera SVG miano formatu graficznego, a przyznaje miano XML (czyli standard, język opisu, a nie format dokumentu).
Racja — użyłem w tym zdaniu trochę zbyt dużego skrótu myślowego.
No właśnie. Co w sytuacji, gdy strona którą testujemy, wprawdzie pozwoli na plik svg, ale umieszcza go w tagu ? Jest bezpiecznie?
Gdy plik jest hostowany na tej samej domenie (lub subdomenie) co testowana strona, to wtedy istnieje niebezpieczeństwo. Odwołanie się do tego pliku bezpośrednio w przeglądarce może spowodować wykonanie złośliwych skryptów.
Przeglądarki wyłączają obsługę javascript w plikach SVG w znaczniku img. Jeśli więc wyłącznie wyświetlamy SVG w img src, wtedy wykonanie złośliwych skryptów nam nie grozi.
Czy nie wystarczy sprawdzić pliku SVG pod kątem XML entities, tagów i tagów ? Wydaje mi się, że wystarczy odrzucić plik jeśli zawiera przynajmniej jedno z wymienionych.
Widzę, że wolicie wylać dziecko z kąpielą. Miało być „… tagów image i tagów script?”, ale wycięliście za dużo ;-)