في الخامس من نوفمبر أصدر فريق OpenSSH الإصدار 10.6p2، المصنَّف حرجًا، لمعالجة تجاوز في تسجيل الدخول لا يحتاج إلى كود استغلال، ولا إلى إفساد في الذاكرة، ولا إلى حمولة ذكية. ترسل كلمة مرور أطول من 256 حرفًا إلى حساب لا يملك كلمة مرور أصلًا. فتدخل.
الخلل لا يسكن في زاوية منسية من الإنترنت. إنه يسكن في Alpine Linux، وبالتالي في حصة كبيرة جدًا من صور الحاويات التي يسحبها فريقك كل أسبوع. وإذا كان لديك مستخدم نشر يعتمد على المفتاح وحده على خادم Alpine تُركت فيه المصادقة بكلمة المرور على وضعها الافتراضي، فإن ذلك الحساب لم يعد يعتمد على المفتاح وحده.
وهنا الجزء الذي يستحق انتباهك: الحسابات المعرّضة للخطر هي تلك التي حصّنتها أنت.
ما يحدث فعليًا على الشبكة
بدون PAM، يتحقق sshd من كلمات المرور بنفسه. يأخذ المحاولة، ويجزّئها عبر crypt()، ثم يقارن الناتج بالحقل المخزَّن في /etc/shadow. الحساب العادي يخزّن شيئًا مثل $6$abc$k2P9...: علامة خوارزمية، وملح، وبصمة.
الحساب الذي لا يملك كلمة مرور يخزّن نجمة واحدة *. تلك العلامة ليست بصمة، ولا يمكن لأي كلمة مرور أن تطابقها. الأمر على هذه الحال منذ عام 1980، حين وضع Bill Joy نجمة في حقل كلمة المرور لحساب daemon في 3BSD. خمس عشرة كلمة من النية، ما زال كل يونكس على وجه الأرض يحترمها.
لكن النجمة أقصر من أن تحمل ملحًا. لذلك يستعير sshd ملحًا: يأخذه من حساب محلي آخر يملك بصمة حقيقية، ويجزّئ محاولتك على أي حال، ثم يقارن. جاءت هذه الاستعارة في يوليو 2016 كإصلاح لـ CVE-2016-6210، وهو أوراكل توقيت كان يسمح للمهاجمين بحصر أسماء المستخدمين الصحيحة. كان إهدار الوقت نفسه على مستخدم غير موجود هو الهدف كله.
أضف الآن عام 2012. مكتبة musl، وهي مكتبة C التي بُني عليها Alpine، غيّرت crypt() بحيث يعيد الاستدعاء الفاشل نجمة واحدة * بدلًا من NULL، لأن برامج كثيرة جدًا لم تكن تتحقق من NULL وكانت تنهار ببساطة. وبعد عشرين يومًا حدّدت musl سقف تجزئة sha256 و sha512 عند 256 بايت لإيقاف هجمات حجب الخدمة التي تغذي المجزّئ بمدخلات ضخمة.
ضع هذه القرارات في عملية واحدة، تنقلب المنطق رأسًا على عقب:
- الحقل المخزَّن هو
*. - كلمة مرورك 257 بايت أو أكثر، لذلك يرفضها
crypt()ويعيد*. - يقارن sshd بين
*و*. - فتتطابقان.
مسار الخطأ هو الباب. ليس خللًا بمعنى كتابة ذاكرة شاردة، بل سلسلة من أربعة قرارات معقولة فرديًا، اتُّخذت بفارق عقود، وهي الآن تتفق على الجواب الخطأ.
من المعرّض فعليًا
يسرد إشعار Alpine أربعة شروط يجب أن تتحقق جميعها:
- أن يشغّل الخادم
openssh-server، وهو مبني بدون PAM، أو أن يشغّلopenssh-server-pamمعUsePAM no. - أن تكون
PasswordAuthenticationعلىyes، وهو الوضع الافتراضي في OpenSSH. - أن يحمل حساب ما
*في حقل كلمة المرور، أي أنه لا توجد كلمة مرور مضبوطة. - أن يملك حساب محلي آخر على الأقل بصمة
$5$أو$6$.
هذا الشرط الأخير هو الهادئ. حساب واحد في أي مكان على الجهاز يملك بصمة كلمة مرور حديثة كافٍ لكشف كل حساب لا يملك واحدة. على Alpine، تشمل الحسابات بلا كلمة مرور المستخدمين المعتمدين على المفتاح الذين أُنشئوا بـ adduser، ومستخدمي الدليل الذين يصلون عبر musl-nscd، وحساب root الذي يُشحن بصيغة root:* منذ Alpine 3.16.
لا يزال root محميًا بـ PermitRootLogin prohibit-password ما لم تغيّره. هذا الدرع يعني أقل مما يبدو إن كان الحساب المكشوف قادرًا على التصعيد: صور tiny-cloud من 3.18.11 حتى 3.24.3 تمنح المستخدم alpine صلاحية doas بلا كلمة مرور. تلك الصور آمنة عند الإقلاع الأول، وتنفتح في اللحظة التي توجد فيها أي بصمة $6$ على الجهاز، وهو ما يحدث أول مرة يضبط فيها مسؤول كلمة مرور للطرفية.
يحتاج المهاجم إلى وصول شبكي، واسم حساب، وكلمة مرور بين 257 و1024 بايت. يقول الإشعار إنها تنجح في كل محاولة، عبر اتصال واحد. ولم تكن قد أُبلغت أي هجمات تستخدمها حين صدر الإصلاح.
غير متأثرة: الأنظمة غير العاملة بـ Linux، وأنظمة Linux المبنية على glibc، وكل ما يتحقق من كلمات المرور عبر PAM، والأنظمة المضبوطة على PasswordAuthentication no، والحسابات المقفلة بـ ! بدلًا من *، والحاويات التي لا تشغّل sshd، والأجهزة التي لا يحمل فيها أي حساب بصمة بصيغة $. وOpenWrt سليم إلى حد كبير لأنه يعتمد افتراضيًا على Dropbear. أما Buildroot و Yocto فيستحقان نظرة.
ما تفعله اليوم
الباحث الذي اكتشفها، Özgün Kültekin من Wakefield، نشر تحليلًا كاملًا على sshhole.com، واللقب الذي التصق بها لا يمكن طباعته في عرض تقديمي لعميل. أما المعالجة فمملة وسريعة:
- حدّث. نفّذ
apk upgrade -U -a، ثمrc-service sshd restart. تحديث الحزمة لا يعيد تشغيل الخدمة العاملة، لذا إعادة التشغيل ليست اختيارية. الإصدارات المصححة: 3.21.10 و 3.22.8 و 3.23.8 و 3.24.4. أما الفروع حتى 3.16 فقد خرجت من الدعم لكنها تلقت حزمًا أيضًا. - إن لم تستطع الترقيع فورًا، فأوقف تسجيل الدخول بكلمة المرور. تأكد من أن دخولك بالمفتاح يعمل في جلسة ثانية قبل أن تغلق الأولى. اضبط
PasswordAuthentication no، ونفّذsshd -t، ثم أعد تشغيل sshd. وتحقق بـsshd -T | grep -i passwordauthentication. - اقتفِ الأثر. ابحث في
/var/log/messagesعنAccepted password forمقابل حساب بلا كلمة مرور:alpineأوrootأو مستخدم نشر يعتمد على المفتاح وحده. راجع استخدامdoas، والمستخدمين الذين أُنشئوا حديثًا، والمفاتيح المصرَّح بها التي عُدّلت، وتغييرات الملفات. - إن لم تستطع استبعاد تسجيل دخول ما، فتعامل مع الجهاز على أنه مخترق. الإشعار بهذا المباشرة، وهو محق.
وإن كنت تبني OpenSSH بنفسك، فإن 10.6p2 يحمل التغيير: يرفض sshd الآن أي تطابق حين يكون الحقل المخزَّن أقصر من أربعة أحرف. ويصف OpenSSH ذلك بأنه حل مؤقت، ويشير إلى musl كسبب جذري.
لماذا وصل الإصلاح في يوم واحد
لم يكن قد خُصص أي معرّف CVE حين صدر الإشعار. وكان Damien Miller صريحًا في سبب تحرك فريقه بهذه السرعة: الحجب الطويل صار خطرًا الآن، وهو يفيد المهاجمين أساسًا، لأن المشروع شاهد مؤخرًا وكلاء ذكاء اصطناعي يعيدون اكتشاف الخلل نفسه، وأحيانًا في غضون ساعات.
اقرأ تلك الجملة مرتين، فهي القصة الحقيقية. الثغرة قرار تصميمي من 2016 يصطدم بتغيير في مكتبة من 2012. أما زمن الاستجابة فتحدده آلات تقرأ الكود طوال الليل وتستطيع إعادة استنتاج نتيجة من أثر عام جزئيًا خلال يوم عمل واحد. كانت نوافذ الحجب تُقاس سابقًا بمدى انتباه البشر. وهي اليوم تُقاس بمعدل تمرير الاستدلال.
ماذا يعني هذا لفريقك
ثلاثة أمور، وأولها وحده يتعلق بـ OpenSSH.
دقّق في الصورة الأساسية، لا في التطبيق فقط. معظم الفرق التي نلتقيها تستطيع تسمية إصدار إطار العمل لديها ولا تستطيع تسمية مكتبة libc. وانتشار Alpine في الحاويات يعني أن إعدادات musl الافتراضية هي إعداداتك الافتراضية، وقد اتخذت musl قرارًا يمكن الدفاع عنه في 2012 صار في 2026 تجاوزًا للمصادقة.
اكتب افتراضاتك كي يمكن تكذيبها. عبارة "الحسابات المعتمدة على المفتاح لا يمكن الدخول إليها بكلمة مرور" كانت صحيحة، وواسعة الاعتقاد، ومنسوخة في مئات الأدلة الداخلية. وقد توقفت عن الصحة دون أن يعدّل أحد تلك الأدلة.
افترض أن أكثر حساباتك هدوءًا هو الحساب المهم. الحساب الذي لا كلمة مرور له، ولا سجل أوامر، ولا مالك على الهيكل التنظيمي. في هذه الثغرة كان هو الهدف، وبصمة $6$ واحدة شاردة في أي مكان على الجهاز حوّلته إلى الباب الأمامي.
رقّع، وأعد التشغيل، وتحقق. ثم اسأل السؤال الذي يدفع فعلًا: أي افتراضاتنا لم يُعد اختباره منذ صارت الآلات أسرع منا؟