01-03-2019 / Owasp

[OWASP Top 10] A4

Cześć!

Witam w kolejnym odcinku z serii OWASP Top 10, w której prześwietlam najpopularniejsze rodzaje błędów pojawiające się w aplikacjach internetowych.

Dzisiaj na tapet weźmiemy nowość - błąd, który pojawił się na liście dopiero w 2017.

A mowa o XXE czyli XML External Entities.

Występuje on, kiedy witryna przetwarza w niebezpieczny sposób pliki XML pochodzące od użytkownika.

Jeżeli chciałbyś zobaczyć przykład takiej podatności w języku Java, zapraszam na jeden z odcinków z serii "od 0 do pentestera"1, gdzie pokazuję podatny kod właśnie w tym języku i tłumaczę jak go naprawić.

Tutaj natomiast chciałbym opisać ten błąd w ogólniejszy sposób, nie skupiając się na konkretnym języku czy też frameworku, ale na powodzie, który sprawia, że taki błąd może pojawić się na naszej stronie internetowej.

XML w wolnym tłumaczeniu to Rozszerzalny Język Znaczników.

Przeznaczony jest do reprezentowania różnych danych w strukturalizowany sposób.

Jest niezależny od platformy, co umożliwia łatwą wymianę dokumentów pomiędzy różnymi systemami.

Standardowo taki plik składa się z elementu głównego, w którym to zawarte są kolejne elementy.

Elementy definiowane są pomiędzy znakiem mniejszości i większości.

Każdy element może posiadać dodatkowe atrybuty, a jego wartość umieszczana jest pomiędzy podwójnymi cudzysłowami.

W ten sposób możemy strukturyzować nasze dane i przenosić je pomiędzy systemami.

Chociażby tworząc główny element pracownicy w którym to znajdują się pojedyncze elementy dotyczące konkretnych pracowników.

Ale jeżeli pracujemy z dużą ilością danych, często zdarza się, że spora część z nich powtarza się w wielu rekordach.

Twórcy XML-a zdawali sobie z tego sprawę - dlatego udostępnili funkcjonalność, która pozwala na obsługiwanie tak powtarzających się sformułowań.

Tutaj nazywane są one encjami.

Taką encję definiuje się pomiędzy tagami ENTITY, nadając jej odpowiednią nazwę i wartość.

Po takiej definicji, możemy używać danej encji w naszych elementach.

Wystarczy użyć ampersanda wraz z nazwą encji, a parser automatycznie podmieni jej treść, na tą wcześniej zdefiniowaną.

Można to więc porównać do makr, na przykład z języka C - kiedy to na samym początku definiujemy jakiś skrót, który będziemy często używać w naszej aplikacji - a potem wykorzystujemy go w kodzie.

Ale popatrzmy na tą funkcjonalność z punktu widzenia atakującego.

Co można przy jej pomocy zepsuć?

Standard nie definiuje maksymalnej liczby encji, które można używać w danym dokumencie.

Encje można również używać rekurencyjnie - to znaczy odwoływać się do innej encji z poziomu encji.

Przykład:

Tworzymy encję dla każdego z kolorów - tak, aby dla tych najpopularniejszych móc używać nazw kolorów zamiast ich reprezentacji w hex.

Niebieski to jest prostszy do wpisania niż #0000ff.

Następnie tworzymy listę encji odnoszące się samochodów, tak aby móc się odnosić do nich skrótami, zamiast za każdym razem podawać całą nazwę.

Teraz możemy stworzyć kolejną encję, na przykład czerwone_bmw.

W niej to wykorzystamy naszą wcześniejszą encję koloru oraz encję odnoszącą się do samochodu - tworząc tym samym jeden ciąg znaków, kolor razem z nazwą modelu.

Teraz za każdym razem, gdy użyjemy encji czerwone_bwm w naszym kodzie, parser automatycznie odniesie się do tych wcześniej zdefiniowanych wartości.

To zachowanie jest fajne z punktu widzenia programisty.

Jeżeli bowiem zmieni się model samochodu - wystarczy zmienić wartość w jednej encji.

Wszystkie encje odnoszące się do tej encji automatycznie wykorzystają nową wartość.

Ale takie encje można zagnieżdżać praktycznie w nieskończoność.

I to jest problem - zwany atakiem miliarda uśmiechów2.

Tworzy się tam 10 encji, w których każda odnosi się do innych encji, które to również odnoszą się do innych encji.

W ten sposób stosunkowo mały plik - po przeprocesowaniu przez parser XML nagle zawiera wiele tysięcy elementów.

Atakujący może więc wysyłając kilka takich małych plików równocześnie, doprowadzić do zawieszenia naszego serwera.

Każdy serwer bowiem zawiera pewną, skończoną liczbę pamięci operacyjnej.

A procesowanie encji i dane związane z tym procesem muszą być gdzieś przechowywane.

W pewnym momencie ilość pamięci RAM może nie wystarczyć.

Ale to nie jedyny problem powiązany z encjami.

Obecnie mówimy jedynie o encjach zdefiniowanych w tym samym pliku, który przesyłamy.

Ale czasami treść encji może być na tyle duża, że chcielibyśmy przechowywać ją w zewnętrznym pliku.

W ten sposób XML zyskuje na czytelności - na samym początku odnosimy się tylko do jednego, zewnętrznego pliku, z którego pobierana jest treść - a nasz plik wejściowy zawiera tylko interesujące nas dane.

I z tym wiąże się drugi problem.

Ten zewnętrzny plik bowiem może być pobrany przez parser z wielu miejsc przy użyciu wielu protokołów.

Może bowiem znajdować się na lokalnym dysku, ale może także znajdować się na zewnętrznym serwerze.

Ale co ma do tego bezpieczeństwo?

Możemy bowiem zdefiniować encję test, która odwoływać się będzie do plików, które powinny zostać tajne.

Dla przykładu pod Linuxem będzie to /etc/passwd oraz /etc/shadow - gdzie zawarte są dane na temat loginów i haseł wszystkich użytkowników.

Parser natrafiając na taką encję - pobierze treść tego tajnego pliku.

A jeżeli przygotujemy nasz złośliwy plik XML w taki sposób, aby używał tej złośliwej encji - treść w naszym pliku zostanie podmieniona na zawartość /etc/passwd.

Jeżeli po przeprocesowaniu zawartość pliku XML jest wyświetlana użytkownikowi - właśnie uzyskał on dostęp do tajnych danych serwera.

W taki sposób nie tylko można pozyskać dane z lokalnego dysku, ale także z innych zasobów sieciowych, które nie są dostępne dla atakującego ponieważ znajdują się za Firewallem.

Mowa tutaj o ataku SSRF - czyli Server Side Request Forgery, kiedy to atakujący uzyskuje dostęp do zasobów na innych serwerach, które nie są dostępne z zewnątrz.

Jest to szczególnie ważne w dzisiejszych czasach, kiedy coraz więcej firm przenosi swoje serwery do chmur obliczeniowych.

Często takie chmury - na przykład Amazon AWS - udostępniają dla lokalnych serwerów prywatne adresy IP, w których znajdują się interesujące dane.

Najbardziej przydatne z punktu widzenia atakującego są tokeny API, które można potem użyć do ataku.

Ponownie jak w przypadku pobierania lokalnego pliku, atakujący tworzy encję, tym razem odnosząc się do innego serwera, wykorzystując protokół HTTP.

Normalnie nie ma do niego dostępu - ponieważ znajduje się on za firewallem.

Ten zasób jest jednak dostępny w tej samej podsieci co serwer podatny na atak SSRF.

Serwer ten zatem może połączyć się z danym adresem HTTP.

Tak to treść żądania od takiego serwera nagle znajduje się w złośliwej encji.

Jak zatem ochronić się przed tym atakiem?

Po pierwsze warto rozważyć zmianę sposobu przesyłania plików.

Dużą popularnością cieszy się teraz JSON, który również można wykorzystać do przesyłania informacji pomiędzy serwerami.

Oczywiście nie zawsze da się zrezygnować z XML-a, zwłaszcza w środowisku korporacyjnym, gdzie jest głęboko zakorzeniony.

Wtedy to warto sprawdzić dokumentację biblioteki3, której używamy i odnaleźć opcję, która wyłączy procesowanie zewnętrznych encji.

Warto również ustawić maksymalny limit rekurencji tak, aby ochronić się przed atakami DDOS.

Ale przesyłanie złośliwych plików XML to nie jedyny powód dlaczego atak XXE znalazł się tak wysoko na liście OWASP.

XML można odnaleźć w wielu innych miejscach - w tym tak kluczowych jak logowanie i autoryzacja użytkownika.

Chodzi mi tutaj o protokół SAML - wykorzystywany do pośredniczenia w uwierzytelnianiu i automatycznego przekazywania między systemami i aplikacjami informacji o uprawnieniach użytkowników.

A tłumacząc to na bardziej zrozumiały język - chodzi o funkcjonalność pojedynczego logowania - SSO.

Weźmy pod uwagę sieć korporacyjną, w której znajduje się wiele różnych systemów.

W standardowym podejściu - każdy system = osobny login i hasła dla użytkownika.

Ale to rodzi wiele trudności. Po pierwsze - jeżeli w systemie pojawi się nowy użytkownik, należy stworzyć konto w każdym z tych systemów.

Ta sama sprawa tyczy się odbierania uprawnień - kiedy to konto należy usunąć, jeżeli pracownik zwolnił się z firmy.

Dlatego popularność zyskuje pojedyncze logowanie przy użyciu protokołu SAML.

Mamy wtedy jeden serwer - nazywany w tej nomenklaturze dostawcą tożsamości.

Taki dostawca odpowiedzialny jest za sprawdzanie loginu i hasła użytkownika.

Co ciekawe takim dostawcą mogą być zewnętrzne serwisy, którym ufamy - na przykład Facebook czy też Google.

Znamy to ze stron, które pozwalają na logowanie się przy pomocy właśnie tych witryn.

Kiedy użytkownik próbuje uzyskać dostęp do jakiegoś zasobu - tutaj zwanego dostawcą treści, podmiot - czyli użytkownik, zostaje przekierowany do dostawcy tożsamości.

Tam następuje logowanie użytkownika i jeżeli wszystko się zgadza - serwer ten zwraca plik XML, w którym znajdują się dane użytkownika.

Oczywiście ten plik jest odpowiednio podpisany, tak aby zapewnić jego integralność.

Następnie ten plik jest przekazywany do dostawcy treści - który weryfikuje jego poprawność i jeżeli wszystko się zgadza - wyświetla żądaną treść.

Jak mogłeś zauważyć - dane pomiędzy dostawcami przekazywane są przy pomocy plików XML.

I tutaj znowu - jeżeli wykorzystano parser, który obsługuje zewnętrzne encje - można odczytać dowolny plik z serwera.

Użytkownik może bowiem przesłać złośliwą zafałszowaną odpowiedź - tak zwany SAMLResponse.

I to wszystko w tym odcinku.

Już teraz zapraszam Cię do kolejnego, w którym omówię nieodpowiedną kontrolę dostępu.

Free Broll provided by videezy.com

Stock footage provided by Videvo, downloaded from https://www.videvo.net

Icon made by Freepik, Smashicons, Dreamstale, Flat-icons-com www.flaticon.com