
مختبر عشوائي لمحرك جافا سكريبت
مُختبر اختراق (Fuzzer) موجه بالتغطية (coverage-guided) لمفسري اللغات الديناميكية، يعتمد على لغة وسيطة مخصصة (FuzzIL) يمكن تحويرها وترجمتها إلى جافاسكريبت.
الخطوات الأساسية لاستخدام هذا المُختبر هي:
swift build [-c release].swift run [-c release] FuzzilliCli --profile=<profile> [other cli options] /path/to/jsshell. راجع أيضًا swift run FuzzilliCli --help.إنشاء وتشغيل فيوزيلي ومحركات جافاسكريبت المدعومة داخل Docker وعلى Google Compute Engine مدعوم أيضًا.
اطلع على main.swift لترى مثالاً على استخدام مكتبة فيوزيلي وتجربة خيارات التكوين المختلفة. بعد ذلك، ألق نظرة على Fuzzer.swift للاطلاع على منطق الاختبار عالي المستوى. من هناك، تعمق في أي جزء يبدو مثيرًا للاهتمام.
التصحيحات والإضافات والمساهمات الأخرى لهذا المشروع مرحب بها جدًا! ومع ذلك، يُرجى مراجعة ملاحظات المساهمين بسرعة. يتبع فيوزيلي تقريبًا دليل أسلوب كود جوجل لـ Swift.
سيكون موضع تقدير كبير إذا أمكنك إرسال ملاحظة قصيرة (قد تتضمن رقم CVE) إلى [email protected] أو فتح طلب سحب (pull request) لأي ثغرة تم العثور عليها بمساعدة هذا المشروع ليتم تضمينها في قسم عرض الثغرات. بخلاف ذلك، يمكنك بالطبع المطالبة بأي مكافأة للثغرات، أرصدة CVE، إلخ للثغرات :)
عند اختبار الأخطاء الأساسية للمفسر، مثل مترجمات JIT، تصبح الصحة الدلالية للبرامج المُنشأة مصدر قلق. وهذا على عكس معظم السيناريوهات الأخرى، مثل اختبار واجهات برمجة التطبيقات في وقت التشغيل، حيث يمكن بسهولة تجاوز مشكلة الصحة الدلالية عن طريق تغليف الكود المُنشأ في بنيات try-catch. هناك احتمالات مختلفة لتحقيق معدل مقبول من العينات الصحيحة دلالياً، أحدها هو النهج التحويري (mutational approach) حيث تكون جميع العينات في المجموعة (corpus) صالحة دلاليًا أيضًا. في هذه الحالة، كل تحوير له فرصة صغيرة فقط لتحويل عينة صالحة إلى عينة غير صالحة.
لتنفيذ مُختبر اختراق جافاسكريبت قائم على التحوير، يجب تعريف التحويرات على كود جافاسكريبت. بدلاً من تحوير شجرة البنية المجردة (AST) أو العناصر النحوية الأخرى للبرنامج، يتم تعريف لغة وسيطة مخصصة (IL) يمكن من خلالها إجراء تحويرات على تدفق التحكم والبيانات بشكل أكثر مباشرة. يتم بعد ذلك ترجمة هذه اللغة الوسيطة إلى جافاسكريبت للتنفيذ. تبدو اللغة الوسيطة تقريبًا كما يلي:
v0 <− LoadInteger '0'
v1 <− LoadInteger '10'
v2 <− LoadInteger '1'
v3 <− LoadInteger '0'
BeginFor v0, '<', v1, '+', v2 −> v4
v6 <− BinaryOperation v3, '+', v4
Reassign v3, v6
EndFor
v7 <− LoadString 'Result: '
v8 <− BinaryOperation v7, '+', v3
v9 <− LoadGlobal 'console'
v10 <− CallMethod v9, 'log', [v8]
والتي يمكن، على سبيل المثال، ترجمتها بسهولة إلى كود جافاسكريبت التالي:
const v0 = 0;
const v1 = 10;
const v2 = 1;
let v3 = 0;
for (let v4 = v0; v4 < v1; v4 = v4 + v2) {
const v6 = v3 + v4;
v3 = v6;
}
const v7 = "Result: ";
const v8 = v7 + v3;
const v9 = console;
const v10 = v9.log(v8);
أو إلى كود جافاسكريبت التالي عن طريق تضمين التعبيرات الوسيطة (inlining):
let v3 = 0;
for (let v4 = 0; v4 < 10; v4++) {
v3 = v3 + v4;
}
console.log("Result: " + v3);
تمتلك FuzzIL عددًا من الخصائص:
Reassign.يمكن بعد ذلك إجراء عدد من التحويرات على هذه البرامج:
يمكن العثور على مناقشة أكثر شمولاً لكيفية عمل فيوزيلي هنا.
المُختبر مُنفذ بلغة Swift، مع بعض الأجزاء (مثل قياسات التغطية، تفاعلات المقابس، إلخ) منفذة بلغة C.
يتكون مثيل المُختبر (منفذ في Fuzzer.swift) من المكونات المركزية التالية:
علاوة على ذلك، تتوفر عدد من الوحدات اختياريًا:
المُختبر مدفوع بالأحداث (event-driven)، حيث تتم معظم التفاعلات بين الفئات المختلفة من خلال الأحداث. يتم إرسال الأحداث، على سبيل المثال، نتيجة لعطل أو العثور على برنامج مثير للاهتمام، أو تنفيذ برنامج جديد، أو إنشاء رسالة سجل، وهكذا. راجع Events.swift للحصول على القائمة الكاملة للأحداث. آلية الأحداث تفصل بشكل فعال المكونات المختلفة للمُختبر وتجعل من السهل تنفيذ وحدات إضافية.
يمكن بناء برنامج FuzzIL باستخدام مثيل ProgramBuilder. يوفر ProgramBuilder طرقًا لإنشاء وإلحاق تعليمات جديدة، وإلحاق تعليمات من برنامج آخر، واسترداد المتغيرات الموجودة، والاستعلام عن سياق التنفيذ في الموضع الحالي (مثل ما إذا كان داخل حلقة)، والمزيد.
يستخدم فيوزيلي وضع تنفيذ مخصص يسمى REPRL (read-eval-print-reset-loop). لذلك، يتم تعديل المحرك الهدف لقبول إدخال نصي عبر الأنابيب و/أو الذاكرة المشتركة، وتنفيذه، ثم إعادة تعيين حالته الداخلية وانتظار النص التالي. هذا يزيل الحمل الزائد الناتج عن إنشاء العمليات وإلى حد كبير عن تهيئة المحرك.
يوجد مثيل Fuzzer واحد لكل عملية هدف. يتيح ذلك التنفيذ المتزامن للبرامج وبالتالي يبسط تنفيذ الخوارزميات المختلفة مثل التحويرات المتتالية والتصغير. علاوة على ذلك، يتجنب الحاجة إلى تنفيذ وصول آمن للخيوط إلى الحالة الداخلية، مثل المجموعة. كل مثيل للمُختبر له DispatchQueue خاص به، والذي يتوافق نظريًا مع خيط واحد. كقاعدة عامة، يجب أن يحدث كل تفاعل مع مثيل Fuzzer على قائمة الانتظار الخاصة به. يضمن ذلك سلامة الخيوط لأن الطابور تسلسلي. لمزيد من التفاصيل راجع الوثائق.
للتوسع، يمكن لمثيلات المُختبر تشكيل هيكل شجري، وفي هذه الحالة يقومون بالإبلاغ عن العينات المثيرة للاهتمام والأعطال التي تم العثور عليها حديثًا إلى العقدة الأم. في المقابل، تقوم العقدة الأم بمزامنة مجموعتها مع العقد التابعة لها. يمكن أن يحدث الاتصال بين العقد في الشجرة بطرق مختلفة، كل منها منفذ كوحدة:
يسمح هذا التصميم للمُختبر بالتوسع إلى العديد من النوى على جهاز واحد وكذلك إلى العديد من الأجهزة المختلفة. نظرًا لأن عقدة أم واحدة يمكن أن تصبح محملة بشكل زائد بسرعة إذا أرسلت إليها مثيلات كثيرة جدًا برامج، فمن الممكن تكوين مستويات متعددة من المثيلات، على سبيل المثال، مثيل جذر واحد، و16 عقدة وسيطة متصلة بالجذر، و256 "ورقة" متصلة بالعقد الوسيطة. راجع دليل Cloud/ لمزيد من المعلومات حول الاختبار الموزع.
موارد إضافية حول هذا المُختبر:
فيما يلي قائمة ببعض الثغرات التي تم العثور عليها بمساعدة فيوزيلي. يجب تضمين الثغرات ذات التأثير الأمني فقط التي كانت موجودة في إصدار بيتا على الأقل من البرنامج المتأثر في هذه القائمة. نظرًا لأن فيوزيلي يُستخدم غالبًا لاختبارات مستمرة أثناء التطوير، فإن العديد من المشكلات التي يعثر عليها غير مدرجة في هذه القائمة لأنها تُعثر عليها عادةً قبل وصول الكود الضعيف إلى إصدار بيتا. ومع ذلك، يمكن العثور على قائمة بجميع المشكلات التي عثر عليها فيوزيلي مؤخرًا في V8 هنا.
شكر خاص لجميع مستخدمي فيوزيلي الذين أبلغوا عن الثغرات التي عثر عليها!
هذا ليس منتجًا مدعومًا رسميًا من Google.