
تحليل فني وتطوير استغلال لـ CVE-2021-3156، وهو تجاوز سعة مخزن مؤقت في الكومة في Sudo، بما في ذلك ثلاثة استغلالات عاملة لتصعيد الامتيازات المحلي على توزيعات Linux الرئيسية.
Qualys Security Advisory
Baron Samedit: تجاوز سعة المخزن المؤقت في الكومة في Sudo (CVE-2021-3156)
الملخص التحليل الاستغلال الشكر الجدول الزمني
اكتشفنا تجاوزًا لسعة المخزن المؤقت في الكومة في Sudo (https://www.sudo.ws/). هذه الثغرة:
قابلة للاستغلال من قبل أي مستخدم محلي (المستخدمون العاديون ومستخدمو النظام، sudoers وغير sudoers)، بدون مصادقة (أي أن المهاجم لا يحتاج لمعرفة كلمة مرور المستخدم)؛
تم إدخالها في يوليو 2011 (commit 8255ed69)، وتؤثر على جميع الإصدارات القديمة من 1.8.2 إلى 1.8.31p2 وجميع الإصدارات المستقرة من 1.9.0 إلى 1.9.5p1، في تكوينها الافتراضي.
قمنا بتطوير ثلاثة استغلالات مختلفة لهذه الثغرة، وحصلنا على صلاحيات الجذر الكاملة على Ubuntu 20.04 (Sudo 1.8.31)، وDebian 10 (Sudo 1.8.27)، وFedora 33 (Sudo 1.9.2). من المحتمل أن تكون أنظمة التشغيل والتوزيعات الأخرى قابلة للاستغلال أيضًا.
إذا تم تنفيذ Sudo لتشغيل أمر في وضع "shell" (shell -c command):
إما عبر الخيار -s، الذي يضع علامة MODE_SHELL الخاصة بـ Sudo؛
أو عبر الخيار -i، الذي يضع علامتي MODE_SHELL و MODE_LOGIN_SHELL الخاصتين بـ Sudo؛
عندها، في بداية main() الخاصة بـ Sudo، تقوم parse_args() بإعادة كتابة argv (الأسطر 609-617)، عن طريق دمج جميع وسائط سطر الأوامر (الأسطر 587-595) وهروب جميع الأحرف الوصفية باستخدام الخطوط المائلة العكسية (الأسطر 590-591):
لاحقًا، في sudoers_policy_main()، تقوم set_cmnd() بدمج وسائط سطر الأوامر في مخزن مؤقت قائم على الكومة "user_args" (الأسطر 864-871) وتفكيك الأحرف الوصفية (الأسطر 866-867)، "لأغراض مطابقة sudoers وتسجيل الدخول":
لسوء الحظ، إذا انتهت وسيطة سطر أوامر بحرف خط مائل عكسي واحد، فعندها:
في السطر 866، "from[0]" هو حرف الخط المائل العكسي، و"from[1]" هو المنهي الصفري للوسيطة (أي ليس حرف مسافة)؛
في السطر 867، يتم زيادة "from" ويشير إلى المنهي الصفري؛
في السطر 868، يتم نسخ المنهي الصفري إلى المخزن المؤقت "user_args"، ويتم زيادة "from" مرة أخرى ويشير إلى الحرف الأول بعد المنهي الصفري (أي خارج حدود الوسيطة)؛
حلقة "while" في الأسطر 865-869 تقرأ وتنسخ أحرفًا خارج الحدود إلى المخزن المؤقت "user_args".
بمعنى آخر، set_cmnd() معرضة لتجاوز سعة المخزن المؤقت في الكومة، لأن الأحرف خارج الحدود التي يتم نسخها إلى المخزن المؤقت "user_args" لم تكن مدرجة في حجمه (المحسوب في الأسطر 852-853).
من الناحية النظرية، ومع ذلك، لا يمكن لأي وسيطة سطر أوامر أن تنتهي بحرف خط مائل عكسي واحد: إذا تم تعيين MODE_SHELL أو MODE_LOGIN_SHELL (السطر 858، شرط ضروري للوصول إلى الكود الضعيف)، فإن MODE_SHELL يتم تعيينها (السطر 571) وقد قامت parse_args() بالفعل بهروب جميع الأحرف الوصفية، بما في ذلك الخطوط المائلة العكسية (أي أنها هربت كل خط مائل عكسي بخط مائل عكسي ثانٍ).
من الناحية العملية، ومع ذلك، فإن الكود الضعيف في set_cmnd() وكود الهروب في parse_args() محاطان بشروط مختلفة قليلاً:
مقابل:
سؤالنا، إذن، هو: هل يمكننا تعيين MODE_SHELL وإما MODE_EDIT أو MODE_CHECK (للوصول إلى الكود الضعيف) ولكن ليس MODE_RUN الافتراضي (لتجنب كود الهروب)؟
الجواب، على ما يبدو، هو لا: إذا قمنا بتعيين MODE_EDIT (خيار -e، السطر 361) أو MODE_CHECK (خيار -l، السطران 423 و519)، فإن parse_args() تزيل MODE_SHELL من "valid_flags" (السطران 363 و424) وتخرج مع وجود خطأ إذا حددنا علامة غير صالحة مثل MODE_SHELL (الأسطر 532-533):
لكننا وجدنا ثغرة: إذا قمنا بتنفيذ Sudo كـ "sudoedit" بدلاً من "sudo"، فإن parse_args() تقوم تلقائيًا بتعيين MODE_EDIT (السطر 270) ولكنها لا تعيد تعيين "valid_flags"، وتتضمن "valid_flags" MODE_SHELL افتراضيًا (السطران 127 و249):
وبالتالي، إذا قمنا بتنفيذ "sudoedit -s"، فإننا نضبط كلاً من MODE_EDIT و MODE_SHELL (ولكن ليس MODE_RUN)، ونتجنب كود الهروب، ونصل إلى الكود الضعيف، ونحدث تجاوزًا لسعة المخزن المؤقت في الكومة "user_args" من خلال وسيطة سطر أوامر تنتهي بحرف خط مائل عكسي واحد: