
仮想的なJWT脆弱性をチェックするためのシンプルなPythonスクリプト。
JWTの仮想的な脆弱性をチェックするためのシンプルなPythonスクリプト。
HS256アルゴリズムで署名されたJWTトークンを使用するアプリケーションがあるとします。トークンの例は次のようになります:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.zbgd5BNF1cqQ_prCEqIvBTjSxMS8bDLnJAE_wE-0Cxg
上記のトークンは以下のデータにデコードできます:
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
署名を計算するために、以下の秘密鍵が使用されています:
supersecret
署名の計算には以下の疑似コードが使用されています:
$alg = "sha256";
$data = "...";
$key = "supersecret";
hmac($alg, $data, $key);
しかし、もし予期しない "kid":0 フィールドがヘッダーに注入されたらどうなるでしょうか?
{
"alg": "HS256",
"typ": "JWT",
"kid": 0
}
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
kid フィールドは鍵を選択するための標準的な方法です。私の仮説では、kid フィールドが想定されていない場合、文字列 $key の値を配列として扱う脆弱な実装が存在する可能性があります:
hmac($alg, $data, $key[kid]);
その結果、"s" ($key[0]) の値がHMACの秘密鍵として使用されます。
injector.py スクリプトは元のJWTトークンを受け取り、 ヘッダーに "kid":0 フィールドを注入し、 1文字の秘密鍵(ASCIIコード: 32 - 126 [{スペース}, !, ", #, ..., x, y, z, {, |, }, ~])で署名されたトークンを生成します:
python3 injector.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.zbgd5BNF1cqQ_prCEqIvBTjSxMS8bDLnJAE_wE-0Cxg
その結果、tokens.txt と tokens_meta.txt の2つのファイルが作成されます。 tokens.txt には生成されたトークンが含まれており、Burp Intruder のペイロードリストとして使用できます。 いずれかのトークンが有効な場合(つまりアプリケーションに脆弱性がある場合)、 tokens_meta.txt ファイルを使用して、特定のトークンの生成に使用されたアルゴリズムと秘密鍵を確認できます。 tokens_meta.txt ファイルには以下のデータが含まれています:
token1:algorithm:secret
...
token{n}:algorithm:secret
CHANGELOG を参照してください。