
Citrix NetScaler SAML प्रमाणीकरण बायपास CVE-2026-19490 का सुरक्षित रूप से पता लगाएं
CVE-2026-19490 के लिए एक सुरक्षित, अनऑथेंटिकेटेड वल्नरेबिलिटी जाँच, जो Citrix NetScaler ADC / NetScaler Gateway SAML सर्विस-प्रोवाइडर पथ में प्री-ऑथेंटिकेशन ऑथेंटिकेशन बायपास है
(CTX696939,
प्रकाशित 2026-08-19)। CWE-288, CVSS v4.0 9.3
(AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L)। JPMorgan Chase पेनिट्रेशन टेस्टिंग टीम के Samarth Vashisht द्वारा रिपोर्ट किया गया।
अप्लायंस SAML रिस्पॉन्स के RelayState पैरामीटर को base64-डिकोड करता है और, जब प्लेनटेक्स्ट ctx= से शुरू होता है, तो शेष भाग को nFactor कॉन्टेक्स्ट डिसेरिएलाइज़र को सौंप देता है। अनपैच्ड बिल्ड पर एक डिसेरिएलाइज़ेशन विफलता त्रुटि के बजाय डिकोड किए गए RelayState की लंबाई को रिक्वेस्ट के आंतरिक डिस्पोज़िशन कोड के रूप में प्रसारित कर देती है, इसलिए एक अनऑथेंटिकेटेड हमलावर केवल यह चुनकर कि RelayState कितना लंबा है, यह तय करता है कि अप्लायंस आगे कौन-सी आंतरिक शाखा लेगा। कुछ शाखाएँ एक वास्तविक Gateway सेशन बनाती हैं; अन्य पैकेट इंजन को क्रैश कर देती हैं और अप्लायंस को रीस्टार्ट कर देती हैं। यह स्क्रिप्ट दोनों में से कुछ नहीं करती — यह वह एक लंबाई भेजती है जिसे सत्यापित किया गया है कि कोई सेशन नहीं बनाती और पैकेट इंजन को अछूता छोड़ती है, और प्रति लक्ष्य एक ही प्रश्न का उत्तर देती है: क्या यह अप्लायंस वल्नरेबल है? VULNERABLE के अलावा कोई परिणाम स्वयं में स्वच्छता का प्रमाण पत्र नहीं है।
# single target
./cve_2026_19490_check.py https://gateway.example.com
# a specific Gateway or AAA virtual server
./cve_2026_19490_check.py https://gateway.example.com:9443
# several targets; the scheme defaults to https://
./cve_2026_19490_check.py gw-a.example.com gw-b.example.com:9443
# scan a list, one target per line ('#' comments allowed), compact output
./cve_2026_19490_check.py -f targets.txt --brief
# machine-readable output for pipelines
./cve_2026_19490_check.py -f targets.txt --json > results.json
Python 3.8+, केवल स्टैंडर्ड लाइब्रेरी — कोई थर्ड-पार्टी पैकेज नहीं।
टूल को Gateway या AAA वर्चुअल सर्वर पर इंगित करें, मैनेजमेंट इंटरफ़ेस पर नहीं। एक्सपोज़र प्रति वर्चुअल सर्वर होता है, इसलिए कई VIP वाले अप्लायंस के प्रत्येक को परखा जाना चाहिए। प्रोब में एक कठोर सुरक्षा लिफ़ाफ़ा है — एक सत्यापित RelayState लंबाई, कभी स्वीप नहीं — जिसे क्या इसे चलाना सुरक्षित है? में बताया गया है।
एक वल्नरेबल अप्लायंस (डिफ़ॉल्ट दो-पंक्ति आउटपुट)। [!] मार्कर और VULNERABLE TTY पर लाल रेंडर होते हैं:
$ ./cve_2026_19490_check.py https://gateway.example.com:9443
[!] https://gateway.example.com:9443: VULNERABLE [internal-error-43524]
HTTP 500 / 43524: the decoded RelayState length was propagated as the internal error, so the CTX696939 fix is absent
एक पैच्ड अप्लायंस:
$ ./cve_2026_19490_check.py https://vpn.example.com
[+] https://vpn.example.com: PATCHED [fixed-error-returned]
HTTP 200 "Malformed Assertion": the fixed error was returned on the path this probe reached, so the CTX696939 fix is present (>= 13.1-63.21 / 14.1-73.32)
फ़ॉल्स-पॉज़िटिव गार्ड का सक्रिय होना। प्रोब और समान-लंबाई वाला कंट्रोल दोनों ने अनपैच्ड सिग्नल लौटाया, इसलिए उत्तर इस पर निर्भर नहीं करता कि क्या भेजा गया था और निर्णायक दिखने वाला उत्तर वापस ले लिया जाता है:
$ ./cve_2026_19490_check.py https://sp-strict.example.com
[?] https://sp-strict.example.com: INCONCLUSIVE [flat-response]
the probe and the same-length control both answered HTTP 500 / 43524, so the reply does not depend on what was sent and the fix was never exercised; unknown, not patched
एक एस्टेट को स्वीप करना (--brief)। दो gateway.example.com पंक्तियाँ एक ही अप्लायंस पर SP और केवल-IdP वर्चुअल सर्वर हैं — दोनों उत्तर देते हैं, जो एक कॉन्फ़िगरेशन-पूर्वशर्त जाँच नहीं कर पाती:
$ ./cve_2026_19490_check.py -f targets.txt --brief; echo "exit: $?"
VULNERABLE https://gateway.example.com:9443 internal-error-43524
VULNERABLE https://gateway.example.com:9444 internal-error-43524
PATCHED https://vpn.example.com fixed-error-returned
INCONCLUSIVE https://sp-strict.example.com flat-response
UNAFFECTED https://lb.example.com no-saml-endpoint
ERROR https://www.example.com not-identified
exit: 1
मशीन-पठनीय आउटपुट (--json)। प्रत्येक रिक्वेस्ट शामिल है, इसलिए किसी निष्कर्ष को भरोसे के बजाय साक्ष्य से पुनः प्राप्त किया जा सकता है। कंट्रोल को उसके स्वयं के निर्णय के रूप में नहीं, बल्कि प्रोब से उसके संबंध द्वारा दर्ज किया जाता है, क्योंकि पैच्ड बिल्ड जैसा पढ़ने वाला कंट्रोल प्रत्येक बिल्ड पर अपेक्षित परिणाम है:
$ ./cve_2026_19490_check.py https://gateway.example.com:9443 --json
[
{
"target": "https://gateway.example.com:9443",
"verdict": "VULNERABLE",
"reason": "internal-error-43524",
"detail": "HTTP 500 / 43524: the decoded RelayState length was propagated as the internal error, so the CTX696939 fix is absent",
"netscaler_indicators": [
"CSP contains citrixng://",
"CSP contains com.citrix.nsgclient://",
"CSP contains nsgcepa://",
"CSP report-uri /nscsp_violation/report_uri",
"/vpn/js/rdx/ present (HTTP 404)"
],
"attempts": [
{
"kind": "probe",
"path": "/cgi/samlauth",
"status": 500,
"state": "unpatched",
"detail": "HTTP 500 / 43524: the decoded RelayState length was propagated as the internal error"
},
{
"kind": "control",
"path": "/cgi/samlauth",
"status": 200,
"state": "differs-from-probe",
"detail": "same-length control: HTTP 200 \"Malformed Assertion\": the fixed error was returned"
}
]
}
]
हाँ। यह प्रोडक्शन और असेसमेंट उपयोग के लिए डिज़ाइन किया गया है:
RelayState ठीक 20 बाइट्स में डिकोड होता है और टूल किसी भी फ़्लैग के अंतर्गत कोई अन्य लंबाई नहीं भेजता। अनपैच्ड अप्लायंस पर डिकोड की गई लंबाई ही शाखा चयनकर्ता है, और शाखाओं में वे भी शामिल हैं जो सेशन बनाती हैं और वे जो पैकेट इंजन को SIGSEGV कर देती हैं — पूरे NetScaler को रीस्टार्ट करके लगभग 45 सेकंड के लिए ट्रैफ़िक गिरा देती हैं। बीस बाइट्स एक स्वच्छ आंतरिक-त्रुटि शाखा पर पहुँचती हैं जो कोई सेशन नहीं बनाती, और पेलोड बिल्डर किसी अन्य लंबाई का निर्माण करने से इनकार कर देता है, इसलिए कहीं और किया गया संपादन चुपचाप प्रोब को नहीं बढ़ा सकता।13.1-63.18 के विरुद्ध लगातार 10 बार चलाया गया, शून्य कोर उत्पन्न हुए और कोई पैकेट-इंजन रीस्टार्ट नहीं हुआ। जाँच स्वयं तब से दोनों मेंटेनेंस शाखाओं के विरुद्ध, फ़िक्स के दोनों ओर, और सर्विस-प्रोवाइडर तथा केवल-IdP वर्चुअल सर्वर दोनों के विरुद्ध चलाई गई है, और उनमें से किसी पर भी कोई पैकेट-इंजन रीस्टार्ट नहीं हुआ।RelayState शाखा तक पहुँचने के लिए SAMLResponse पैरामीटर का मौजूद होना आवश्यक है, लेकिन इसकी सामग्री अप्रासंगिक है, इसलिए यह चार बाइट्स का कचरा है। शाखा कोई कुकी जारी नहीं करती और कोई कॉन्फ़िगरेशन नहीं लिखती।VULNERABLE कभी भी एकल प्रतिक्रिया पर रिपोर्ट नहीं किया जाता — देखें कंट्रोल ही है जो 500 को अर्थपूर्ण बनाता है।यदि आप प्रोब को संशोधित करते हैं, तो डिकोड की गई
RelayStateलंबाई न बदलें और लंबाइयों को स्वीप न करें। बीस बाइट्स भार-वहन करने वाली है, और कम होना अधिक सुरक्षित नहीं है। अन्य लंबाइयाँ सर्वोत्तम स्थिति में अप्रयुक्त हैं और सबसे बुरी स्थिति में अप्लायंस आउटेज हैं।
प्रति लक्ष्य तीन HTTP रिक्वेस्ट, और चौथी केवल तब जब अप्लायंस अनपैच्ड सिग्नल लौटाता है:
रीडायरेक्ट का पालन नहीं किया जाता और कोई कुकी कभी वापस नहीं भेजी जाती — NetScaler एक समाप्त NSC_DLGE परोसता है जिसे रीप्ले करने वाला क्लाइंट रिक्वेस्ट 4 में ले जाएगा, जिससे POST लॉगन पेज में बदल जाएगा।
nsppe SAML POST को /var/log/ns.log में सूचनात्मक स्तर पर लॉग करता है, किसी डीबग नॉब की आवश्यकता नहीं, एक nFactor कॉन्टेक्स्ट डिसेरिएलाइज़ेशन विफलता के रूप में जिसमें पंक्ति शामिल है
nFactor: ctx deserialize aaa_info, base64 decode failed। इस लंबाई पर कोई सेशन-निर्माण या लॉगिन पंक्तियाँ नहीं हैं, जो एक स्कैन को बायपास के प्रयास से अलग करता है।
फ़िक्स एक निर्देश है। विफल कॉन्टेक्स्ट डिसेरिएलाइज़ेशन पर वल्नरेबल बिल्ड डिकोड की गई RelayState लंबाई को रिक्वेस्ट की स्थिति के रूप में आगे बढ़ाता है; फ़िक्स्ड बिल्ड स्थिर त्रुटि 0xe0005 को आगे बढ़ाता है, जिसे Malformed Assertion के रूप में रेंडर किया जाता है। इसलिए एक POST उन्हें अलग करता है:
| प्रोब को प्रतिक्रिया | बिल्ड | निर्णय |
|---|---|---|
500 Internal Server Error 43524 | अनपैच्ड | VULNERABLE |
200 Malformed Assertion sent to Netscaler | पैच्ड | PATCHED |
पैच्ड बिल्ड प्रत्येक लंबाई पर Malformed Assertion का उत्तर देता है, जो अनपैच्ड उत्तर को संयोग के बजाय एक ओरेकल बनाता है।
VULNERABLE एकल विभेदक प्रतिक्रिया पर टिका है, इसलिए टूल सत्यापित करता है कि प्रतिक्रिया वास्तव में इस पर निर्भर करती है कि क्या भेजा गया था। अनपैच्ड सिग्नल के बाद यह रिक्वेस्ट को समान 20-बाइट डिकोड की गई लंबाई के कंट्रोल RelayState के साथ दोहराता है, जो केवल ctx= के बाद के 16 बाइट्स में भिन्न होता है। उस सामग्री को डिसेरिएलाइज़र से पहले एक गार्ड द्वारा अस्वीकार कर दिया जाता है, इसलिए परीक्षण किए गए प्रत्येक बिल्ड — पैच्ड और अनपैच्ड — इसका उत्तर 200 Malformed Assertion देते हैं। निर्णय तभी टिकता है जब दोनों उत्तर भिन्न हों। यदि वे मेल खाते हैं, तो कुछ भी इनपुट की परवाह किए बिना 500/43524 लौटा रहा है, और परिणाम झूठे VULNERABLE के बजाय flat-response टैग के साथ INCONCLUSIVE होता है। कंट्रोल को समान डिकोड की गई लंबाई पर रखना इसे सत्यापित सुरक्षा लिफ़ाफ़े के भीतर रखता है और सामग्री को एकमात्र चर के रूप में अलग करता है।
गार्ड एकदिशीय है। यह PATCHED निर्णय को मान्य नहीं कर सकता, क्योंकि वास्तव में पैच्ड अप्लायंस प्रोब और कंट्रोल का समान उत्तर देता है — यहाँ पैच्ड होने का यही अर्थ है। देखें सीमाएँ।
RelayState डिसेरिएलाइज़ेशन SAML पॉलिसी मिलान से पहले चलता है, इसलिए केवल samlIdPPolicy वाला वर्चुअल सर्वर भी प्रोब का उत्तर देता है, न कि केवल वह जिसमें सर्विस-प्रोवाइडर samlAction बाउंड हो। यह कॉन्फ़िगरेशन-पूर्वशर्त जाँच की तुलना में व्यापक कवरेज है।
वल्नरेबल उत्तर ही एकमात्र स्व-पुष्टिकारक उत्तर है: यह एक विशिष्ट मार्कर रखता है, और कंट्रोल सिद्ध करता है कि उत्तर इस पर निर्भर करता है कि क्या भेजा गया था। PATCHED भी सकारात्मक साक्ष्य है — अप्लायंस की स्वयं की फ़िक्स्ड त्रुटि उस पथ पर जिस तक प्रोब पहुँचा — लेकिन यह इस CVE और उस पथ तक सीमित है। INCONCLUSIVE और ERROR बिल्कुल भी साक्ष्य नहीं हैं: प्रोब कभी डिसेरिएलाइज़र तक नहीं पहुँचा, इसलिए पैच स्थिति अज्ञात है, और इसीलिए INCONCLUSIVE एक अलग निर्णय है न कि PATCHED में समाहित। मौन को पास के रूप में रिपोर्ट करना इसे खतरनाक दिशा में गलत करता है। जहाँ आपको निश्चितता चाहिए, उपचार में दिए गए बिल्ड्स के विरुद्ध show ns version से पुष्टि करें।
प्रत्येक निर्णय में एक संक्षिप्त reason टैग होता है। --brief इसे तीसरे कॉलम के रूप में प्रिंट करता है और --json इसे reason के रूप में रखता है।
निर्णय-निर्माण के लिए सभी चार INCONCLUSIVE कारणों का एक ही अर्थ है — अज्ञात, पैच्ड नहीं। टैग बताता है कि पुनः चलाने से पहले कौन-सी स्थिति ठीक करनी है।
| कोड | अर्थ |
|---|---|
0 | कोई लक्ष्य VULNERABLE नहीं था |
1 | कम से कम एक लक्ष्य VULNERABLE है |
2 |
एग्ज़िट 0 स्वच्छता का प्रमाण पत्र नहीं है। PATCHED, UNAFFECTED और सभी चार INCONCLUSIVE कारण इसमें समाहित हो जाते हैं, और उनमें से केवल एक सकारात्मक परिणाम है। "पैच्ड" को "वर्गीकृत नहीं किया जा सका" से अलग बताने वाले रैपर को निर्णय पढ़ना चाहिए — --brief का पहला कॉलम, या --json का verdict फ़ील्ड — एग्ज़िट कोड नहीं।
PATCHED निर्णय को विकृत कर सकता है। Malformed Assertion वही है जो अप्लायंस तब लौटाता है जब प्रोब कॉन्टेक्स्ट डिसेरिएलाइज़र तक पहुँचने में विफल रहता है, और पैच्ड होना ऐसा होने का केवल एक कारण है। इसलिए RelayState पैरामीटर को हटाने, विकृत करने, या शॉर्ट-सर्किट करने वाला इंटरपोज़्ड डिवाइस एक वल्नरेबल अप्लायंस पर PATCHED उत्पन्न करता है, और टूल जो भी अतिरिक्त रिक्वेस्ट भेज सकता है वह दोनों को अलग नहीं करेगा। जहाँ अप्लायंस के सामने कुछ हो सकता है, वहाँ show ns version से पुष्टि करें।UNAFFECTED वर्तमान बिल्ड्स तक सीमित है। 14.1-43.55 / 13.1-61.27 और उससे पहले पर एडवाइज़री बताती है कि एक्सपोज़र के लिए SAML action की आवश्यकता बिल्कुल नहीं है — कोई भी Gateway या AAA वर्चुअल सर्वर प्रभावित है — इसलिए ऐसे बिल्ड पर 404 अप्लायंस को दायरे से बाहर नहीं करता। वह भाग मापन के बजाय एडवाइज़री से लिया गया है।PATCHED किसी अन्य NetScaler वल्नरेबिलिटी के बारे में कुछ नहीं कहता, जिसमें उसी बुलेटिन में CVE-2026-19489 भी शामिल है।13.1-63.21 या बाद के संस्करण, या 14.1-73.32 या बाद के संस्करण में अपग्रेड करें (FIPS और NDcPP: 13.1-FIPS के लिए 13.1-37.277 और 13.1-NDcPP, 14.1-FIPS के लिए 14.1-73.32 FIPS), CTX696939 के अनुसार। 12.1 या 13.0 पर मौजूद अप्लायंस के लिए कोई फ़िक्स नहीं है और न ही मिलेगा — वे शाखाएँ एंड ऑफ़ लाइफ़ हैं और उन्हें समर्थित शाखा में माइग्रेट किया जाना चाहिए।
तीन और नोट:
| फ़्लैग | विवरण |
|---|
TARGET | एक या अधिक [https://]HOST[:PORT] लक्ष्य; स्कीम डिफ़ॉल्ट रूप से https:// है |
-f, --targets-file FILE | किसी फ़ाइल से लक्ष्य पढ़ें (प्रति पंक्ति एक; # टिप्पणियाँ) |
--timeout SECS | प्रति-रिक्वेस्ट टाइमआउट (डिफ़ॉल्ट: 15) |
--workers N | समवर्ती लक्ष्य (डिफ़ॉल्ट: 16); आउटपुट इनपुट क्रम में रहता है |
-b, --brief | प्रति लक्ष्य एक संरेखित पंक्ति — कई होस्ट स्कैन करने के लिए आदर्श |
--json | संरचित JSON उत्पन्न करें, जिसमें प्रति लक्ष्य भेजी गई प्रत्येक रिक्वेस्ट शामिल हो |
--no-color | रंगीन आउटपुट अक्षम करें (NO_COLOR और गैर-TTY का भी सम्मान करता है) |
| # | रिक्वेस्ट | उद्देश्य |
|---|
| 1 | GET /logon/LogonPoint/tmindex.html | NetScaler पहचान (Content-Security-Policy टोकन) |
| 2 | GET /vpn/js/rdx/core/rdx.js | NetScaler पहचान |
| 3 | POST /cgi/samlauth | प्रोब |
| 4 | POST /cgi/samlauth | कंट्रोल, केवल अनपैच्ड सिग्नल के बाद भेजा जाता है |
| निर्णय | कारण टैग | अर्थ |
|---|
VULNERABLE | internal-error-43524 | अप्लायंस ने डिकोड की गई RelayState लंबाई को अपनी आंतरिक त्रुटि के रूप में प्रसारित किया, और समान-लंबाई वाले कंट्रोल ने सिद्ध किया कि उत्तर इस पर निर्भर करता है कि क्या भेजा गया था। CTX696939 फ़िक्स अनुपस्थित है — इसे पैच करें। |
PATCHED | fixed-error-returned | अप्लायंस ने उस पथ पर फ़िक्स्ड त्रुटि लौटाई जिस तक प्रोब पहुँचा। इस CVE तक सीमित, और इंटरपोज़्ड डिवाइस पर सीमाएँ देखें। |
UNAFFECTED | no-saml-endpoint | /cgi/samlauth ने 404 लौटाया, इसलिए SAML assertion consumer यहाँ परोसा नहीं जाता। प्रति-vserver, प्रति-अप्लायंस नहीं, और सीमाएँ में पुराने-बिल्ड की चेतावनी देखें। |
INCONCLUSIVE | flat-response | प्रोब और समान-लंबाई वाले कंट्रोल ने समान उत्तर दिया, इसलिए उत्तर इस पर निर्भर नहीं करता कि क्या भेजा गया था। फ़ॉल्स-पॉज़िटिव गार्ड का सक्रिय होना। |
INCONCLUSIVE | generic-internal-error | एंडपॉइंट ने 43549 लौटाया, इसकी सामान्य आंतरिक त्रुटि, जो पैच्ड और अनपैच्ड दोनों बिल्ड लौटाते हैं। विभेदक नहीं — और ध्यान दें कि यह असंबंधित CVE-2026-8452 के लिए अनपैच्ड मार्कर है, इसलिए यह हिट जैसा दिखता है और है नहीं। |
INCONCLUSIVE | unrecognized-reply | एंडपॉइंट ने मान्यता प्राप्त सेट के बाहर कुछ उत्तर दिया। |
INCONCLUSIVE | no-probe-response | होस्ट ने पहचान का उत्तर दिया लेकिन प्रोब का नहीं — एक टाइमआउट, एक रीसेट, या POST को गिराने वाला मिडलबॉक्स। पुनः प्रयास करें। |
ERROR | not-identified | NetScaler के रूप में पहचाना नहीं गया, या अगम्य। |
| उपयोग त्रुटि (खराब आर्ग्युमेंट / अपठनीय targets फ़ाइल) |
VULNERABLE/var/log/ns.log