مع بداية عام 2019 ، من الجيد أن نتذكر الماضي والتفكير في المستقبل. دعنا ننظر إلى الوراء لمدة 30 عامًا ونتأمل في المقالات العلمية الأولى عن التحسس:
"دراسة تجريبية لموثوقية
أدوات UNIX" وما تلاها من أعمال 1995
"مراجعة Fuzzing" للمؤلف نفسه
بارتون ميلر .
في هذه المقالة ، سنحاول العثور على أخطاء في الإصدارات الحديثة من Ubuntu Linux باستخدام
نفس الأدوات المستخدمة في أعمال التجميع الأصلية. يجب عليك قراءة المستندات الأصلية ليس فقط للسياق ، ولكن أيضًا لفهمها. لقد اتضح أنها نبوية للغاية فيما يتعلق بالضعف وتستغل لعقود قادمة. قد يلاحظ القراء المهتمون تاريخ نشر المقال الأصلي: 1990. حتى أكثر اهتماما ستلاحظ حقوق التأليف والنشر في تعليقات المصدر: 1989.
مراجعة قصيرة
بالنسبة لأولئك الذين لم يقرؤوا الوثائق (على الرغم من أنه ينبغي القيام بذلك فعلاً) ، يحتوي هذا القسم على ملخص موجز وبعض الاقتباسات المحددة.
يولد
البرنامج المتدفق تدفقات عشوائية من الأحرف ، مع القدرة على إنشاء أحرف قابلة للطباعة أو غير قابلة للطباعة فقط. يستخدم قيمة مبدئية معينة (البذور الأولية) ، مما يضمن نتائج قابلة للتكرار ، والتي غالبًا ما تفتقر إليها أجهزة fuzzers الحديثة. يتم تشغيل مجموعة من البرامج النصية على البرامج المختبرة والتحقق من وجود مقالب أساسية. تم الكشف عن تعليق يدويا. توفر المحولات مدخلات عشوائية للبرامج التفاعلية (مقالة 1990) ، وخدمات الشبكة (1995) ، وتطبيقات X الرسومية (1995).
اختبرت مقالة عام 1990 أربعة معالجات للمعالجات (i386 و CVAX و Sparc و 68020) وخمسة أنظمة تشغيل (4.3 BSD و SunOS و AIX و Xenix و Dynix). في مقال 1995 ، اختيار مماثل للمنصات. في المقالة الأولى ، تفشل 25-33٪ من الأدوات المساعدة ، اعتمادًا على النظام الأساسي. في مقال لاحق ، تتراوح هذه الأرقام من 9 ٪ إلى 33 ٪ ، مع وجود غنو (على SunOS) ولينكس بأقل معدل فشل.
خلصت مقالة 1990 إلى أن 1) لا يتحقق المبرمجون من حدود الصفيف أو رموز الخطأ ، 2) تجعل وحدات الماكرو صعوبة في قراءة التعليمات البرمجية وتصحيحها ، و 3) C غير آمن للغاية. وظيفة غير آمنة للغاية وتم ذكر نظام النوع C. بشكل خاص ، أثناء الاختبار ، وجد المؤلفون نقاط ضعف Format String قبل سنوات من استغلالهم الجماعي. تختتم المقالة بمسح للمستخدمين حول عدد المرات التي يصلحون فيها الأخطاء أو يبلغون عنها. اتضح أن الإبلاغ عن الأخطاء كان صعبًا ولم يكن هناك اهتمام كبير بإصلاحها.
يشير مقال 1995 إلى برمجيات المصدر المفتوح ويناقش سبب وجود أخطاء أقل. اقتباس:
عندما حققنا في أسباب الفشل ، ظهرت ظاهرة مقلقة: العديد من الأخطاء (حوالي 40 ٪) التي تم الإبلاغ عنها في عام 1990 لا تزال موجودة في شكلها الدقيق في عام 1995. ...
الأساليب المستخدمة هنا بسيطة ومعظمها الآلي. من الصعب فهم السبب وراء عدم استخدام المطورين لهذا المصدر السهل والمجاني لزيادة الموثوقية.
في غضون 15-20 عامًا فقط ، ستصبح تقنية الدمج ممارسة قياسية للبائعين الكبار.
يبدو لي أيضًا أن بيان 1990 هذا يتوقع أحداثًا مستقبلية:
في كثير من الأحيان ، يتم أخذ النمط اللغوي للبرمجة C إلى أقصى الحدود ، ويسود النموذج على الوظيفة الصحيحة. احتمال حدوث تجاوز في المخزن المؤقت للمدخلات هو ثغرة أمنية محتملة ، كما أظهرت دودة الإنترنت الأخيرة .
منهجية الاختبار
لحسن الحظ ، بعد مرور 30 عامًا ، لا يزال الدكتور بارتون يوفر
شفرة المصدر الكاملة والبرامج النصية والبيانات لإعادة إنتاج النتائج التي توصل إليها : مثال جدير بالثناء يجب على الباحثين الآخرين اتباعه. تعمل البرامج النصية دون مشاكل ، وتتطلب أداة التحجيم تغييرات طفيفة فقط للتجميع والتشغيل.
بالنسبة لهذه الاختبارات ، استخدمنا
البرامج النصية والمدخلات من مستودع fuzz-1995 الأساسي ، لأن هناك أحدث قائمة من
التطبيقات المختبرة . وفقًا لـ
README ، إليك نفس المدخلات العشوائية كما في الدراسة الأصلية. يتم الحصول
على النتائج أدناه لنظام التشغيل Linux الحديث
تمامًا بنفس شفرة التقديم وإدخال البيانات كما في المقالات الأصلية. تم تغيير قائمة الأدوات المساعدة للاختبار فقط.
التغييرات فائدة أكثر من 30 عاما
من الواضح ، على مدى الثلاثين عامًا الماضية ، كانت هناك بعض التغييرات في حزم برامج Linux ، على الرغم من أن عددًا قليلاً من الأدوات المساعدة التي أثبتت جدواها استمرت لعقود طويلة. حيثما أمكن ، أخذنا إصدارات حديثة من نفس البرامج من مقالة 1995. بعض البرامج لم تعد متوفرة ، قمنا باستبدالها. مبررات جميع البدائل:
cfe
cc1
: أي ما يعادل المعالجات C من مقالة 1995.dbx
gdb
: ما يعادل مصحح الأخطاء 1995.ditroff
groff
: لم يعد ditroff
متاحًا.dtbl
gtbl
: أي ما يعادل GNU Troff dtbl
القديمة dtbl
.lisp
clisp
: التنفيذ القياسي لـ lisp.more
⇨ less
: الأقل أكثر!prolog
swipl
: هناك خياران للبروج: SWI Prolog و GNU Prolog. SWI Prolog هو الأفضل لأنه تطبيق أقدم وأكثر اكتمالا.awk
⇨ gawk
: نسخة جنو من awk
.cc
⇨ gcc
: المترجم C القياسي.compress
z gzip
: GZip هو السليل المفاهيمي لأداة compress
يونكس القديمة.lint
⇨ splint
: إعادة كتابة lint
تحت GPL./bin/mail
⇨ /usr/bin/mail
: أداة مكافئة بطريقة مختلفة.f77
fort77
: هناك نوعان مختلفان من المترجم Fortan77: GNU Fortran و Fort77. يُنصح باستخدام الإصدار الأول من نظام Fortran 90 ، والثاني لدعم Fortran77. f2c
دعم برنامج f2c
بشكل نشط ؛ تم الاحتفاظ بقائمة التغييرات الخاصة به منذ عام 1989.
النتائج
1989 تقنية fuzzing لا يزال يجد أخطاء في 2018. ولكن هناك بعض التقدم.
لقياس التقدم ، تحتاج إلى بعض الأساس. لحسن الحظ ، يوجد مثل هذا الإطار لأدوات Linux المساعدة. على الرغم من أن نظام Linux لم يكن موجودًا في وقت كتابة المقال الأصلي في عام 1990 ، إلا أن اختبارًا آخر في عام 1995 قد أطلق نفس التعليمات البرمجية المذهلة على الأدوات المساعدة من توزيع Slackware 2.1.0 لعام 1995. وترد النتائج المقابلة في
الجدول 3 من المادة 1995 (ص 7-9) . بالمقارنة مع المنافسين التجاريين ، يبدو جنو / لينكس جيدًا للغاية:
كانت نسبة تعطل الأداة المساعدة في إصدار Linux المجاني من UNIX في المرتبة الثانية: 9٪.
لذلك ، دعونا نقارن الأدوات المساعدة لنظام التشغيل لينوكس لعامي 1995 و 2018 بالأدوات المبهرة لعام 1989:
| أوبونتو 18.10 (2018) | أوبونتو 18.04 (2018) | أوبونتو 16.04 (2016) | أوبونتو 14.04 (2014) | سلاكوير 2.1.0 (1995) |
---|
تعطل | 1 (ص 77) | 1 (ص 77) | 2 (F77 ، ul) | 2 (الضلع ، F77) | 4 (ul ، المرن ، المسافة البادئة ، gdb) |
يتجمد | 1 (تهجئة) | 1 (تهجئة) | 1 (تهجئة) | 2 (الإملائي ، وحدات) | 1 (علامات) |
مجموع اختبارها | 81 | 81 | 81 | 81 | 55 |
الفشل / يتجمد ، ٪ | 2٪ | 2٪ | 4٪ | 5٪ | 9٪ |
والمثير للدهشة أن عدد حالات تعطل وتجميد Linux لا يزال أكبر من الصفر ، حتى في أحدث إصدار من Ubuntu. لذلك ، يستدعي
f77
برنامج
f2c
مع وجود خطأ تجزئة ، ويتم تعليق برنامج
spell
على نسختين من إدخال الاختبار.
ما البق؟
تمكنت من اكتشاف السبب الجذري لبعض الأخطاء يدويًا. بعض النتائج ، مثل خطأ في glibc ، كانت غير متوقعة ، بينما يمكن التنبؤ بنتائج أخرى ، مثل sprintf بحجم ثابت للمخزن المؤقت.
فشل ماي
الخطأ في
ul هو في الواقع خطأ في glibc. على وجه الخصوص ، تم الإبلاغ
هنا وهنا (شخص آخر وجدها في
ul
) في عام 2016. وفقًا لتعقب الأخطاء ، لا يزال الخطأ غير ثابت. نظرًا لأنه لا يمكن إعادة إنتاج الخلل في Ubuntu 18.04 والإصدارات الأحدث ، يتم إصلاحه عند مستوى التوزيع. اذا حكمنا من خلال التعليقات على تعقب الأخطاء ، يمكن أن تكون المشكلة الرئيسية خطيرة للغاية.
تحطم F77
يأتي برنامج
f77
في الحزمة fort77 ، التي هي في حد ذاتها برنامج نصي shell حول
f2c
، والمترجم المصدر من Fortran77 إلى C. يوضح تصحيح
f2c
أن الفشل يحدث عندما تقوم دالة
errstr
بطباعة رسالة خطأ طويلة جدًا. يُظهر
رمز مصدر f2c أنه يتم استخدام وظيفة sprintf لكتابة سلسلة متغيرة الطول إلى مخزن مؤقت ذي حجم ثابت:
errstr(const char *s, const char *t) #endif { char buff[100]; sprintf(buff, s, t); err(buff); }
يبدو أنه تم الحفاظ على هذا الرمز منذ إنشاء
f2c
. البرنامج له
تاريخ من التغيير منذ عام 1989 على الأقل. في عام 1995 ، عند إعادة الدمج ، لم يتم اختبار برنامج التحويل البرمجي Fortran77 ، وإلا فربما تم العثور على المشكلة مسبقًا.
تجميد الإملائي
مثال رائع على الجمود الكلاسيكي.
spell
مندوبو "
ispell
spell
بتفويض
ispell
عبر أنبوب.
spell
يقرأ سطر النص بسطر وينتج سجلاً مانعًا لحجم السطر في
ispell
. ومع ذلك ، تقرأ
ispell
بحد أقصى
BUFSIZ/2
بايت في وقت واحد (4096 بايت على
BUFSIZ/2
) وتصدر سجل حظر للتأكد من أن العميل قد تلقى بيانات التحقق من الصحة التي تمت معالجتها حتى الآن. أجبر مدخلا اختبار مختلفان
spell
على كتابة سلسلة من أكثر من 4096 حرفًا لـ
ispell
، مما أدى إلى توقف تام: تنتظر
spell
ispell
بقراءة السلسلة بالكامل ، بينما تنتظر
ispell
spell
لتأكيد أنها قد قرأت تصحيحات الهجاء الأصلية.
شنق الوحدات
للوهلة الأولى ، يبدو أن هناك حالة حلقة لا نهائية. يبدو أن
libreadline
في
libreadline
وليس في
units
، على الرغم من أن الإصدارات الأحدث من
units
لا تعاني من هذا الخطأ. يشير سجل التغيير إلى أنه تمت إضافة تصفية الإدخال التي يمكن أن تؤدي إلى حل هذه المشكلة عن طريق الخطأ. ومع ذلك ، فإن التحقيق الشامل للأسباب يتجاوز نطاق هذه المدونة. ربما لا تزال طريقة تعليق
libreadline
موجودة.
تحطم الضرب
من أجل الاكتمال ، أود أن أذكر حادث تحطم
swipl
، على الرغم من أنني لم
swipl
بعناية ، لأن الخلل قد تم إصلاحه منذ فترة طويلة ويبدو أنه عالي الجودة إلى حد ما. الفشل هو في الواقع عبارة (أي بيان يجب ألا يحدث أبدًا) يتم استدعاؤها عند تحويل الأحرف:
[Thread 1] pl-fli.c:2495: codeToAtom: Assertion failed: chrcode >= 0
C-stack trace labeled "crash":
[0] __assert_fail+0x41
[1] PL_put_term+0x18e
[2] PL_unify_text+0x1c4
…
دائمًا ما يكون التعطل سيئًا ، ولكن على الأقل يمكن للبرنامج الإبلاغ عن خطأ ، حيث يتعطل مبكرًا وبصوت عالٍ.
الخاتمة
على مدار الثلاثين عامًا الماضية ، ظل الإندماج طريقة بسيطة وموثوقة للعثور على الأخطاء. على الرغم من أن
الأبحاث النشطة جارية في
هذا المجال ، إلا أن fuzzer قبل 30 عامًا نجح في العثور على أخطاء في أدوات Linux الحديثة.
تنبأ مؤلف المقالات الأصلية بمشاكل الأمان التي قد تسببها C على مدار العقود القادمة. يجادل بشكل مقنع أن الكود غير الآمن من السهل جدًا كتابته في لغة C ويجب تجنبه إن أمكن. على وجه الخصوص ، توضح المقالات أن الأخطاء تظهر حتى مع أبسط مراحل ، وينبغي إدراج مثل هذا الاختبار في ممارسة تطوير البرمجيات القياسية. لسوء الحظ ، لم يتم اتباع هذه النصيحة منذ عقود.
آمل أن تستمتع هذا 30 سنة بأثر رجعي. انتظر المقال التالي "Fuzzing in 2000" ، حيث سنبحث في كيفية مقارنة تطبيقات Windows 10 القوية
بمكافئات Windows NT / 2000 الخاصة بها عند اختبارها باستخدام fuzzer . أعتقد أن الجواب يمكن التنبؤ به.