29-03-2019 / Owasp

[OWASP Top 10] A8

Cześć.

To już kolejny odcinek z serii OWASP Top 10, w której opisuję najpopularniejsze podatności powiązane ze stronami internetowymi.

Dzisiaj o niebezpiecznej deserializacji obiektów.

W większości języków programowania możemy tworzyć obiekty - czyli instancje klas.

Każdy z obiektów może przechowywać jakieś dane.

Czasami istnieje potrzeba, aby te dane gdzieś zapisać.

Serializacja to sposób na przechowywanie obiektów lub struktur tak, aby mogły być łatwo zachowane i transmitowane.

Serializacja to generyczna nazwa i w różnych językach programowania te nazwy mogą się nieco różnić.

W Ruby, to "marshalling", w go "gobbing" a w Python "pickling".

Z niebezpieczną deserializacją mamy do czynienia jeżeli to użytkownik kontroluje zawartość zserializowanego obiektu.

Serwer, aby przetworzyć tak zapisany obiekt, musi bowiem wykonać operację odwrotną, nazywaną deserializacją.

Programiści wiedzą że dane od użytkownika należy odpowiednio procesować tak, aby zapobiegać na przykład atakom typu SQL Injection, które opisywałem w pierwszym odcinku.

Zdarza się jednak, że zapominają o odpowiednim filtrowaniu takich danych - jeżeli pochodzą one z serializowanego obiektu.

Rozpatrzmy to na przykładzie.

Standardowo zalogowany użytkownik jest rozpoznawany przez serwer bazując na ciasteczku zawierającym identyfikator sesji.

Na podstawie tego identyfikatora serwer pobiera z pliku sesji wszystkie dane na temat danego użytkownika i udziela mu dostępu do odpowiednich funkcjonalności.

To rozwiązanie ma pewien minus - słabo się skaluje.

Każdy serwer bowiem obsługujący daną funkcjonalność musi mieć dostęp do plików sesji - aby na ich podstawie określić z kim ma do czynienia.

Uwzględniając te minusy stworzono nowy protokół JWT - gdzie dane na temat sesji użytkownika są przesyłane bezpośrednio przez przeglądarkę w każdym żądaniu.

Podczas logowania serwer tylko raz sprawdza login i hasło i jeżeli wszystko się zgadza generuje token JWT - w którym zawarte są wszystkie informacje na temat danego użytkownika.

Dzięki temu serwer, który realizuje daną funkcjonalność, nie musi przechowywać danych sesji na swoim dysku.

JWT to skrót od JSON Web Tokens - dane przechowywane są więc w postaci plików JSON.

Jeżeli użytkownik je kontroluje - może zatem podmienić identyfikator użytkownika w takim pliku przesyłanym do serwera.

Ponieważ teraz serwer sprawdza użytkownika na podstawie tych danych, a nie danych z sesji na własnym dysku - użytkownik nagle może stać się administratorem.

Wystarczy bowiem zmienić login z kacper na admin w pliku json i już - stajemy się innym użytkownikiem.

Dlatego też te tokeny są odpowiednio podpisane.

Taki podpis gwarantuje integralność danych czyli to, że dane w tokenie nie zostały zmienione przez nikogo.

Dzięki temu to rozwiązanie jest bezpieczne, dopóki nie wycieknie klucz, który został użyty do podpisywania tokenów.

Zakładamy oczywiście, że weryfikowanie tego podpisu nie posiada żadnych błędów w implementacji.

Tutaj - bez podpisu użytkownik kontrolował informacje na temat loginu, na podstawie którego serwer udzielał dostępu do panelu administracyjnego.

Ale przecież wszystko zależy od aplikacji jaką tworzymy.

W niektórych dane z takich obiektów mogą być przekazywane bezpośrednio do zapytań SQL - i jeżeli tam odpowiednio ich nie zabezpieczymy - dochodzi do ataków SQL Injection.

Albo też mogą być po prostu wyświetlane na stronie.

Chociażby, gdyby sklep w takim obiekcie przechowywał sumaryczną kwotę koszyka z naszymi zakupami.

Złośliwy użytkownik zmieniając dane w tym obiekcie na tekst - doprowadza do ataku XSS - czyli wykonania kodu JavaScript.

A to może mieć miejsce jeżeli programista ufa danym które są zserializowane i zapomina, że w specyficznych warunkach mogą być kontrolowane przez atakującego.

Ale to nie całość historii.

W niektórych językach bowiem podczas procedury deserializajci, język automatycznie wywołuje pewne metody na nowo tworzonym obiekcie.

W PHP te metody nazywane są magicznymi i zaczynają się od podwójnego podkreślenia - na przykład __wakeup.

W Javie może to być natomiast metoda readObject.

W Ruby potrzebujemy metody marshal_load.

Ale co nam to daje, że podczas deserializacji stworzonego przez nas obiektu dana metoda jest automatycznie wykonywana?

Aby to zrozumieć musimy wprowadzić pojęcie Gadżetu.

Gadżety są to fragmenty kodu aplikacji, które podczas normalnego jej działania nie są szkodliwe.

Chociażby usuwanie plików z danego katalogu, który został przekazany jako parametr do danej funkcji.

Atakujący może połączyć takie gadżety w jeden ciąg i wykorzystać go w inny sposób niż zamierzony.

Ta metoda jest wykorzystywana zwłaszcza w exploitacji plików binarnych w tak zwanym Return Oriented Programming.

Cała idea ataku opiera się zatem na znalezieniu podatnej magicznej metody, która wykonuje inne fragmenty kodu - które złączone razem w jedną całość doprowadzą do wykonania jakiejś złośliwej akcji.

I zapewne słysząc to po raz pierwszy możesz nie do końca rozumieć o co chodzi.

Rozpatrzmy zatem dalej przykład wykorzystując nasz gadżet służący do usuwania plików tymczasowych.

W metodzie readObject zaimplementowano dodatkowe logowanie - tak, aby wszystkie dane przetwarzane przez tą metodę były zapisywane do osobnych katalogów - aby w przypadku jakichś błędów można było sprawdzić co poszło nie tak.

Dodatkowo, aby tych plików nie przechowywać za dużo - jeżeli ich ilość przekroczyła 10 - automatycznie usuwany jest najstarszy katalog.

A więc w ostatecznym rozrachunku, podczas normalnego wykonywania aplikacji - jeżeli plików było więcej niż 10 - Java wykonywała nasz gadżet, służący do usuwania konkretnego katalogu.

Tylko, że podczas deserializacji mogliśmy ustawić zmienną, przekazywaną do tego gadżetu na dowolną, kontrolowaną przez nas wartość.

Użyliśmy zatem funkcji w inny sposób niż pierwotnie zakładano, sprawiając tym samym, że można usunąć dowolne pliki z danego serwera.

Samo tworzenie i znajdowanie takich gadżetów to temat na odrębny film.

Nie zawsze trzeba ich jednak szukać samemu.

W Internecie można odnaleźć generyczne Gadżety - czyli takie złączenie metod z różnych popularnych bibliotek, które na samym końcu prowadzi do wykonania kodu.

W Javie takie gadżety odnaleziono w Apache Commons Collections.

Jeżeli więc nasza aplikacja wykorzystuje tą bibliotekę możemy być podatni.

W przypadku PHP dostępne są gadżety dla ZendFramework czy też Symfony.

Na samym końcu chciałbym wspomnieć o jeszcze jednym temacie powiązanym z tym rodzajem podatności.

Chodzi tutaj o funkcje, które wykonują kod do nich przekazany.

Mowa o eval, dostępnych w PHP czy też JavaScript.

Tutaj exploitacja jest jeszcze prostsza - wystarczy tylko kontrolować część ciągu przekazywanego do tej funkcji.

Ciekawostką może być również fakt, że deserializacja może występować w miejscach i formatach, w których nigdy byśmy się tego nie spodziewali.

Za przykład niech posłuży funkcja input z języka Python, która teoretycznie ma pobierać tekst od użytkownika, a tak naprawdę dodatkowo wykonuje na nim funkcję eval.

Lub też biblioteka YAML, w której przy pomocy object/apply można wywołać komendy systemowe.

Więcej na ten temat dowiesz się na filmach z cyklu "od 0 do pentestera".

Jak zatem ochronić się przed tą klasą błędów?

Odpowiedź nie jest taka prosta i zależy od języka.

Najlepiej byłoby nie korzystać z serilizacji, ale to nie zawsze możliwe.

Innym podejściem może być podpisywanie obiektów.

Wtedy deserializacja powinna mieć miejsce dopiero, gdy sprawdzimy poprawność podpisu.

Czasami istnieją również alternatywne funkcje lub całe biblioteki, które nie pozwalają na wywoływanie potencjalnie złośliwych akcji.

Warto zatem za każdym razem przeglądnąć odpowiednią dokumentację.

I to już wszystko w tym odcinku.

A w przedostatnim materiale z tego cyklu dowiesz się o używaniu komponentów ze znanymi podatnościami.

W międzyczasie zapraszam do posłuchania podcastu Szurkogadanie, w którym opowiadam co ostatnio działo się w branży bezpieczeństwa.

Jeżeli ten materiał Ci się spodobał, nie zapomnij o subskrypcji kanału i zostawieniu łapki w górę.

Do zobaczenia.

Cześć!

Free stock footage by Videezy.com

Business vector created by freepik - www.freepik.com

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

Icon made by Smashicons, Prosymbols, Flat Icons, Freepik www.flaticon.com