
مختبر إثبات مفهوم يوضح تجاوز مصادقة acceptInvite الخطير في Lago GraphQL الذي يتيح الاستيلاء على الحسابات عبر المؤسسات من خلال قبول الدعوات دون مصادقة.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · lago-invite-ato
التحليل المكتوب: https://abraxaslabs.tech/research/lago-invite-ato
Lago v1.53.0 - GetLago
لا يحتوي Mutations::Invites::Accept على AuthenticableApiUser. أما Create/revoke فتحتوي عليه. Accept لا. تقوم register_from_invite بتحميل المستخدم عبر بريد الدعوة الإلكتروني. إذا كان ذلك الحساب يمتلك عضويات بالفعل، فسيتم تجاهل كلمة المرور المُرسَلة. ومع ذلك يظل AuthToken.encode(user:) يُنفَّذ. الـ JWT مقيَّد بالمستخدم. لا يوجد ادعاء org. يرسل العميل x-lago-organization لأي org ينتمي إليه ذلك المستخدم بالفعل. بما في ذلك org الضحية. createInvite يُعيد بالفعل الرمز إلى المُدعِي.
ادعُ cfo@victim إلى org مؤقتة، واقبل الدعوة بنفسك، وستصبح ذلك الشخص على كل org يوجد فيها ذلك البريد الإلكتروني بالفعل. صندوق البريد لا يُستخدم إطلاقًا. كلمة مرور الضحية دون تغيير.
| المعرّف | لا يوجد CVE بعد |
| CWE | CWE-287، CWE-306 |
| CVSS | حرج: 9.6 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N |
| المنتج | Lago / lago-api |
| المتأثر | حتى v1.53.0 acceptInvite عبر البريد الإلكتروني/كلمة المرور |
| المصادقة | مسؤول org (organization:members:create)؛ التسجيل في Cloud كافٍ |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
تحتاج إلى org تتحكم بها. التسجيل في Cloud كافٍ (LAGO_DISABLE_SIGNUP قيمته الافتراضية false). ادعُ بريدًا إلكترونيًا للضحية، واقبل الدعوة دون مصادقة بكلمة مرور وهمية، ثم بدّل الترويسة إلى Victim Corp. الفواتير، ومفاتيح API، وStripe، والمحافظ.
ليس زائرًا عشوائيًا بلا حساب. وليس إعادة تعيين كلمة المرور. تسجيل الدخول الوهمي يفشل. كلمة مرور الضحية الحقيقية لا تزال تعمل. قبول Google/Okta يربط بريد IdP وليس هذه الثغرة. الاستضافة الذاتية أحادية الـ org حيث لا يوجد بريد شخص آخر في قاعدة البيانات لا يمنحك تاجرًا آخر. نفس قاعدة البيانات، وorg اثنتان، ونفس البريد الإلكتروني: فإنه يمنحك ذلك.
نفس الشجرة التي تنتمي إليها ثغرة IDOR في معرّف فاتورة Stripe لمرة واحدة. ثغرة مختلفة.
قرأت Accept (لا يوجد AuthenticableApiUser)، ثم find_or_initialize_by(email:)، ثم الفرع الذي توجد فيه العضويات والذي يتخطى كلمة المرور، ثم AuthToken.encode(user:). ثم قبول Google، الذي يتحقق من google_oidc["email"] == invite.email. أما القبول عبر البريد الإلكتروني/كلمة المرور فلا يربط كلمة مرور مستخدم موجود.
أول عميل ينظر إلى هذا سيدعو عنوانًا غير مستخدم، ويقبله، ويحصل على مستخدم Lago جديد تمامًا بكلمة المرور التي أرسلها. تلك هي الميزة. الدليل هو عودة معرّف مستخدم الضحية الموجود بالفعل.
مسارات خاطئة مسجَّلة بالفعل: القبول باستخدام Google/Okta؛ اعتبار خطأ 401 في createInvite بدون JWT هو القصة كاملة (create مُصادَق عليه، وaccept ليس كذلك)؛ النظر فقط إلى org المهاجم في currentUser (الدليل هو Victim Corp في القائمة)؛ تسجيل الدخول بكلمة المرور الوهمية (هذا يعني أن التجزئة قد كُتبت فوقها - يؤكد المختبر أنها لم تُكتب فوقها)؛ reverse shell. مسرح. لا صندوق بريد.
ثم: استدعاءان لـ registerUser يزرعان Victim Corp وAttacker Corp. بريد إلكتروني جديد في كل تشغيل حتى لا يُرجع حجم بيانات قذر خطأ 422 user_already_exists. createInvite على بريد الضحية الإلكتروني. acceptInvite غير مصادَق عليه بكلمة مرور وهمية. معرّف المستخدم المُعاد يطابق الضحية. currentUser.organizations يسرد كليهما. تبديل الترويسة يُرجع Victim Corp.
cd lab
./run.sh
استهدف فقط http://127.0.0.1:13000. استُخدم Stripe IDOR على 13001. لا يوجد منفذ واجهة مستخدم.
IOC victim_password_unchanged
IOC victim_real_password_still_works
SUCCESS LAGO-INVITE-ATO acceptInvite JWT is existing victim; lists Victim Corp; no inbox
إذا كان المستخدم موجودًا بالفعل، فاطلب منه المصادقة بهويته (أو إثبات ملكية صندوق البريد / موفّر الهوية) قبل ربط العضوية. لا تُصدر JWT لمستخدم موجود من قبول غير مُصادَق عليه. توقّف عن إرجاع token الدعوة إلى المُدعِي إذا كان يمثّل صلاحية وصول.
accept.rb · accept_service.rb · users_service.rb · create.rb · object.rb · google_service.rbGNU Affero GPL v3.0. راجع LICENSE. مختبر الاسترجاع المحلي فقط. دون أي ضمان.