Scroll to top

GHSA-c522-hqhp-v2qq: ثغرة حرجة في uucore تتيح لملف ترجمة حقن شيفرة في أدوات coreutils المكتوبة بـ Rust

  • Home
  • المدونة
uucore FTL translation injection vulnerability GHSA-c522-hqhp-v2qq

الإرشاد الأمني: GHSA-c522-hqhp-v2qq
مستوى الخطورة: حرجة · CVSS 9.9 (v3.1)
المتّجه: AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
نوع الضعف: CWE-94 (حقن الشيفرة)، CWE-1395 (الاعتماد على مكوّن خارجي ضعيف)
الحزمة المتأثرة: uucore (crates.io) من 0.2.2 حتى 0.11.0
الإصدار المُصحِّح: 0.12.0

uutils coreutils هو إعادة كتابة متعددة المنصّات بلغة Rust لأدوات GNU الأساسية — ls وcp وcat وinstall وkill وغيرها — وأصبح يُشحن الآن كحزمة coreutils الافتراضية في توزيعات Linux الرئيسية. وكل واحدة من هذه الأدوات ترتبط بحزمة uucore المشتركة.

اكتشفتُ أن سكربت البناء الخاص بـ uucore كان يحوّل ملفات الترجمة في المشروع إلى شيفرة Rust بطريقة غير آمنة. كان ملف ترجمة واحد بصيغة Fluent ‏(.ftl) يحتوي على المحرفين "# كافيًا للخروج من السلسلة النصية الحرفية، فيُترجَم باقي الملف كشيفرة Rust داخل الملفات التنفيذية.

بعبارة أخرى، كان ملف يعامله كل مراجع على أنه نص غير ضار يمثّل عمليًا طريقًا لزرع باب خلفي في سلسلة التوريد داخل الأدوات الأساسية.


كيف تُضمَّن ملفات الترجمة

تعتمد uutils على Project Fluent لترجمة رسائلها. ولجعل الملفات التنفيذية مكتفية ذاتيًا، يقرأ src/uucore/build.rs كل ملف .ftl وقت الترجمة ويكتبه داخل ملف Rust مُولَّد اسمه embedded_locales.rs كفرع من فروع تعبير match:

// src/uucore/build.rs (vulnerable versions)
// Determine if we need a hash. If content contains ", we need r#""#
let delimiter = if content.contains('"') { "#" } else { "" };
writeln!(
    embedded_file,
    "        \"{locale_key}\" => Some(r{delimiter}\"{content}\"{delimiter}),"
)?;

ثم يُدرج هذا الملف المُولَّد مباشرةً داخل الحزمة التي تعتمد عليها كل الأدوات:

// src/uucore/src/lib/mods/locale.rs
include!(concat!(env!("OUT_DIR"), "/embedded_locales.rs"));

أي محتوى ينتهي به المطاف في embedded_locales.rs هو شيفرة Rust عادية، تُترجَم بكامل صلاحيات البرنامج.


السبب الجذري: السلسلة الخام تنتهي عند "# لا عند "

سلسلة Rust الخام التي تُفتح بعلامة # واحدة، r#"، لا تعرف تسلسلات الهروب، بل تنتهي ببساطة عند أول زوج من البايتات "#.

كان الشرط في build.rs يسأل فقط عمّا إذا كان المحتوى يحتوي على علامة اقتباس مزدوجة، ولم يتحقق قط مما إذا كان يحتوي على علامة الإنهاء نفسها. لذا فإن أي ترجمة تحتوي على " يليها مباشرةً # تُغلق السلسلة مبكرًا، ويُحلَّل كل ما بعدها بواسطة rustc كشيفرة.


إثبات المفهوم

يكفي ملف ترجمة بهذا المحتوى. فبالنسبة لـ Fluent، هو مجرد رسالة اسمها greeting:

# evil.ftl
greeting = quote "#),
        "pwn" => { std::process::Command::new("/bin/sh").arg("-c")
            .arg("id > /tmp/UUTILS_FTL_INJECTION_PROOF").status().ok(); None },
        "tail" => Some(r#"

بعد أن يغلّفه build.rs داخل r#"…"#، يصبح المصدر المُولَّد:

        "<locale_key>" => Some(r#"greeting = quote "#),
        "pwn" => { std::process::Command::new("/bin/sh").arg("-c")
            .arg("id > /tmp/UUTILS_FTL_INJECTION_PROOF").status().ok(); None },
        "tail" => Some(r#""#),

تنتهي السلسلة بعد quote ، ويظهر فرع match جديد ينفّذ أمر صدفة، ثم يبتلع فرع وهمي أخير علامة الإغلاق الأصلية. يُترجَم الملف دون أخطاء، وتصبح الحمولة جزءًا من مكتبة uucore المرتبطة بكل أداة.

فرع match ليس سوى أبسط مثال. ولأن المهاجم يتحكم في رموز Rust الخام، يمكن للنص المحقون أن يُغلق الدالة ويضيف عناصر عشوائية خاصة به. وقد أكّدتُ وجود المشكلة على فرع master الحالي، لا على شيفرة قديمة مخزّنة مؤقتًا.


التأثير: اختراق uutils عبر طلب دمج خبيث

يمكن لهذه الثغرة أن تؤدي إلى اختراق uutils نفسه عبر طلب دمج خبيث واحد. فلا يحتاج المهاجم إلى صلاحيات الإيداع ولا إلى الوصول إلى حساب أي مشرف، بل يكفيه أن يُدمَج تعديل ترجمة يبدو غير ضار.

بمجرد دمج طلب الدمج هذا، تُترجَم شيفرة المهاجم في الإصدار الرسمي وفي كل حزمة توزيعة تُبنى منه. ولأن uutils يوفّر الأوامر الأساسية التي تشغّلها باستمرار سكربتات النظام وبرامج التثبيت ومديرو الحزم وخطوط CI، فإن ذلك سيكون كارثة شاملة في سلسلة التوريد: برمجيات خبيثة تُشحن داخل ملفات نظام تنفيذية موثوقة وموقّعة على كل جهاز يثبّتها أو يحدّثها.


لماذا كانت قابلة للاستغلال واقعيًا

  • يحتوي شجرة المصدر على 214 ملف .ftl، و34 منها كانت تحتوي أصلًا على "، فكانت تسلك فرع r#"…"# بالفعل. ولم يكن ينقص سوى محرف # إضافي.
  • كان الملف .github/fluent_linter_config.yml يعطّل صراحةً فحص علامات الاقتباس المزدوجة TE04 في مدقق Fluent، فكانت علامات الاقتباس في الترجمات مسموحة بموجب سياسة المشروع، ولم يكن هناك ما يبحث عن هذا النمط في CI.
  • يركّز المراجعون على ملفات .rs. والتعديل في سطر واحد داخل ملف ترجمة — خاصةً ضمن طلب دمج كبير — يسهل قبوله دون نظرة ثانية.
  • بمجرد دمجها، تُترجَم الحمولة في الإصدار التالي وفي كل حزمة توزيعة تُبنى منه، فتصل إلى كل نظام يشغّل تلك الملفات التنفيذية. ولهذا جاء التقييم بـ نطاق متغيّر (S:C) مع تأثير كامل على السرية والسلامة والتوافر.

الإصلاح

اقترح بلاغي التخلّي عن السلاسل الخام كليًا، وترك منسّق Debug في Rust يُنتج سلسلة حرفية مُهرَّبة بشكل صحيح. وقد اعتمد المشرفون هذا الحل تحديدًا، وهو متاح في uucore 0.12.0:

// src/uucore/build.rs (fixed)
// Never a raw string: `r#"..."#` ends at `"#`, so content holding that
// pair closes the literal and the rest is compiled as Rust. `{:?}`
// escapes every quote and has no terminator the content can reach.
writeln!(
    embedded_file,
    "        {locale_key:?} => Some({content:?}),"
)?;

مع {:?} تُهرَّب كل علامات الاقتباس والشرطات المائلة العكسية ومحارف التحكم، فتصبح سلسلة الاستغلال بيانات خاملة داخل سلسلة حرفية عادية.


ما الذي ينبغي فعله

  • قم بالترقية إلى uutils coreutils / uucore بالإصدار 0.12.0 أو أحدث.
  • مُحزِّمو التوزيعات وكل من يبني من المصدر: أعيدوا البناء من شجرة مُصحَّحة، وافحصوا أي ملفات ترجمة لاحقة أو مُضمَّنة.
  • دقّقوا ملفات الترجمة بحثًا عن زوج الإنهاء:
grep -rnF '"#' --include='*.ftl' .

ينطبق الدرس الأوسع على أي مشروع يحتوي على build.rs أو مولّد شيفرة أو قالب: لا تبنِ الشيفرة المصدرية أبدًا بلصق البيانات داخل سلسلة نصية. استخدم آلية تهريب حقيقية مثل {:?} أو quote! أو أداة تسلسل، وتعامل مع كل ملف يقرؤه سكربت البناء كجزء من الشيفرة التي تحتاج إلى مراجعة.


التقييم النهائي

GHSA-c522-hqhp-v2qq خطأ صغير ذو نطاق تأثير كبير. فغياب فحص محرف واحد في سكربت بناء حوّل ملفات الترجمة — أقل الملفات تدقيقًا في المستودع — إلى وسيلة لحقن شيفرة في الأدوات الأساسية التي تقوم عليها معظم سكربتات الصدفة في النظام.

شكرًا لمشرفي uutils على الإصلاح السريع والنشر المنسّق.


المراجع

إرشاد GitHub الأمني
GHSA-c522-hqhp-v2qq

uutils coreutils
https://github.com/uutils/coreutils

مرجع Rust — السلاسل الحرفية الخام
https://doc.rust-lang.org/reference/tokens.html#raw-string-literals

CWE-94: التحكم غير السليم في توليد الشيفرة
https://cwe.mitre.org/data/definitions/94.html

اكتشاف الثغرة والإبلاغ عنها: JoeCyberTech (GitHub: HackingRepo)

اترك تعليقًا

يرجى التحقق من بريدك الإلكتروني
يرجى التحقق من رسالتك
شكرًا لك. تم إرسال رسالتك.
خطأ، لم يتم إرسال البريد

الأكثر قراءة