🕐

jwkパラメータによる自己署名JWTの注入、なんも分からん。

2022/08/07に公開

先日先輩に手取り足取り教えていただいたにも関わらず全然分からず、、、今日美容院行ってぼーっとしてる時にこうじゃないか?って思って考えたらなんとなくそんな気がしてきたので、月曜日に答え合わせするべく書く・・・
かなり自分用なので読んでいただいても参考にはなりません_(._.)_

これ↓
https://portswigger.net/web-security/jwt#:~:text=Injecting self-signed JWTs via the jwk parameter

JWTとは

JSON Web トークン (JWT) は、暗号で署名された JSON データをシステム間で送信するための標準化された形式です。理論的にはあらゆる種類のデータを含めることができますが、認証、セッション処理、およびアクセス制御メカニズムの一部として、ユーザーに関する情報 (「クレーム」) を送信するために最も一般的に使用されます。従来のセッション トークンとは異なり、サーバーが必要とするすべてのデータは、クライアント側で JWT 自体に格納されます。これにより、JWT は、ユーザーが複数のバックエンド サーバーとシームレスに対話する必要がある高度に分散された Web サイトで一般的な選択肢になります。
https://portswigger.net/web-security/jwt#:~:text=Issued definitions tab.-,What are JWTs%3F,-JSON web tokens より

詳しい説明はいろんな人が書いてくれているので割愛。
参考:https://jwt.io/introduction

JWTの形式

ヘッダー.ペイロード.署名で構成される。

  • ヘッダーは「こういう署名方法にするよ~」という情報をBase64エンコードしたものが(主として)入ってる
  • ペイロードにユーザを識別する情報をBase64エンコードしたものが入ってる
  • (ヘッダーとペイロードの値をハッシュ化して署名して出来上がる。次に説明。)

参考:3. JSON Web Signature (JWS)

JWTを使ったセッション管理だとペイロード部分に「"name":"wiener"」とかユーザの情報が入っているのが、通常のセッションIDとかと違うね~というところ

Base64デコードすると"sub":"wiener"が見える

(JWEはまた構成が違うけど割愛。参考:4. JSON Web Encryption (JWE)

JWTの署名

(JWTはここがまじで天才!!)
「え、ペイロード部分にユーザに関する情報が入っているなら、"name":"administrator"とかにすればなりすまし出来るんじゃないか!?」と思うけど、それが出来ないように署名部分というものがある。署名部分は前述の通り、『ヘッダー.ペイロードをハッシュ化した後に署名して出来上がるもの』で、

  • ヘッダー.ペイロード部分が1バイトでも違うと、署名が一致しない
  • サーバの秘密鍵を知らなければ、正しい署名を作ることは出来ない

ので、ペイロード部分を少しでも変えると401 unauthorizedのような認証ダメでした的なレスポンスが返ってくるし、じゃあ正しい署名で"name":"administrator"にペイロード部分を変えたいとなっても秘密鍵が分からなければ正しい署名は出来ないから不正できないよねっ!っていう仕組み

"sub":"administrator"に変えても署名が一致していないので401レスポンスになっている

("alg":"none"はサポートしないでね。参考:https://scgajge12.hatenablog.com/entry/jwt_security#311-algnone-攻撃

JWTの署名の検証(RSA256)

  1. 私が普通にログインする(username:wiener, password:peter)
  2. サーバがusername, passwordあっているか確認。あっているのでセッションIDを発行する。この時にサーバしか知らない秘密鍵を用いて署名を作成、『ヘッダー.ペイロード.署名』のセッションIDを発行する
  3. レスポンスが返ってくる。Set-cookieされる。(Set-Cookie: session=ヘッダー.ペイロード.署名;)
  4. 私が普通にリクエストする(session=ヘッダー.ペイロード.署名;)
  5. サーバが送られてきたJWTの署名を検証する(ここ違う気がする)(ハッシュ化したヘッダー.ペイロードが公開鍵でハッシュ値に戻した署名と一致するか確認)。
  6. 一致したので、正常なレスポンスが返ってくる

参考:https://www.jipdec.or.jp/project/research/why-e-signature/public-key-cryptography.html, 公開鍵暗号の数理(1回目)の5ページ目

すごいざっくり言うと、『サーバの秘密鍵で署名を作成』して、『サーバの公開鍵で署名が合っているか検証』するということをしてる。ここが大事

本題。jwkパラメータによる自己署名JWTの注入

ラボに出ているjwkパラメータインジェクションは『攻撃者が用意した秘密鍵で生成した署名』を『jwkパラメータを追加してリクエスト』することで『サーバがjwkパラメータの(攻撃者が用意した)公開鍵で検証』してしまうので起こる

1.~3. 通常と一緒
4. 悪い人は秘密鍵と公開鍵を作成(これはサーバ側には全く関係ないやつ)
5. ヘッダにjwkパラメータを追加、ペイロードのユーザー名部分を"wiener"から"administrator"に変更(burpのExtension使うときはjwkパラメータを追加するのは自動でやってくれるのでユーザ名変えるだけ)
6. 悪い人が作った自作の秘密鍵で5.の署名を作成
7. 『session=jwkを追加したヘッダ.wienerからadministratorに変更したパラメータ.自作の秘密鍵で作成した署名』でリクエスト
8. 『サーバ側はヘッダ部分にあったjwkパラメータにある公開鍵を使って』検証(ここが脆弱性。本当はここでサーバ側の公開鍵を使わなければならない)
9. 一致するので、サーバ側から管理者画面のレスポンスが返ってくる

余談

RSA署名は秘密鍵で署名→公開鍵で検証という使い方、RSA暗号は公開鍵で暗号化→秘密鍵で復号という使い方ができるスゲーやつなんだ!というのがとても面白かった↓
https://qiita.com/angel_p_57/items/4989494481cbf3c61f04#ナイーブなrsaの実装

実際に公開鍵、秘密鍵をどうやって作るかというのもやってみると面白かった↓
http://herb.h.kobe-u.ac.jp/RSA.html

暗号とか数学的部分がさっぱり分からなかった私にとってこの記事が分かりやすかった↓
https://go-journey.club/archives/14072

迷走した気がするけど暗号の仕組みが面白かった~!!間違ってたら修正します_(._.)_

Discussion