はじめに
2026年3月20日時点、ネットで見つかる多くのWriteUpではブルートフォース攻撃による攻略が一般的ですが、環境やサーバー負荷の影響でどうしても突破できないケースがあります。
今回、別の視点から「正攻法ではない回避策」を見つけたので、シェアしたいと思います。
1. リークデータ user.db の解析
まずは、与えられたリークデータ user.db から、ユーザー名とパスワードのハッシュ値を確認します。
ヒントに 「What happens when there's no salt?」 とある通り、パスワードハッシュにソルト(Salt)が使われていないようです。
2.レインボーテーブル攻撃
ソルトがないハッシュ値は、計算済みの対応表(レインボーテーブル)を使えば一瞬で逆引きできます。
CrackStation などのサイトを利用することで、admin のパスワードを特定できました。
3. 本当の攻略法:Flaskセッションの解読
さて、この記事の本題はここからですが、
adminのアカウントでログインした後は、ワンタイムパスワードを求められます。
1000~9999までの4桁の数字なので、これはブルートフォース攻撃が通用すると思いました。実際、ほかのネット上のWriteUpもブルートフォース攻撃でしたが、私がどうしても突破できなくて、泣く泣く別の方法を探しました。
ソースコードを凝視すると、以下の1行に重大な脆弱性がありました。
session['otp_secret'] = otp
セッションは「クライアント側のCoockie」に保存されていて、sessionの中身を解読すれば、普通にワンタイムパスワードが取得できます!
取得したCookieの例:
session=.eJwty0sKgCAQANC7zFoiMRr1MiE5ieAPnVbR3XPR9sF7INUQyIOFy6VBIKByOwadnXiiNDv-xjHTYJcbWImotEIp1WJW1FpvAu5BvbhMMzmfY4H3A0gLHG8.abqbOQ.fakL5JN8KE0aTHct6SRJ5pjxhFA;
4.セッションCookieの仕組み
FlaskのセッションCookieが .(ドット)と eJw で始まっている場合、それはデータがzlibで圧縮された後にBase64エンコードされているサインです。
これを解読(Unsign)すれば、わざわざ通信を発生させなくても中身が丸見えになります。
以下のコードで、ブラウザから取得したCookieのペイロード部分を解析できます。
import base64
import zlib
# 最初のドットを除いたペイロード部分(.eJw... の eJw 以降)
payload = "eJwty0sKgCAQANC7zFoiMRr1MiE5ieAPnVbR3XPR9sF7INUQyIOFy6VBIKByOwadnXiiNDv-xjHTYJcbWImotEIp1WJW1FpvAu5BvbhMMzmfY4H3A0gLHG8"
# Base64のパディング(=)を補完し、デコードしてzlibで解凍
try:
# URLセーフなBase64としてデコード
compressed = base64.urlsafe_b64decode(payload + "===")
decompressed = zlib.decompress(compressed)
print("Decoded Session:", decompressed.decode('utf-8'))
except Exception as e:
print(f"Error: {e}")
これを実行すると、以下のようなJSONデータが出力されます。
{"_fresh": true, "logged": "false", "otp_secret": "XXXX", "otp_timestamp": 1742487000.0, "username": "admin"}

