معهد ماساتشوستس للتكنولوجيا. محاضرة رقم 6.858. "أمن أنظمة الكمبيوتر." نيكولاي زيلدوفيتش ، جيمس ميكنز. 2014 سنة
أمان أنظمة الكمبيوتر هي دورة حول تطوير وتنفيذ أنظمة الكمبيوتر الآمنة. تغطي المحاضرات نماذج التهديد ، والهجمات التي تهدد الأمن ، وتقنيات الأمان بناءً على العمل العلمي الحديث. تشمل الموضوعات أمان نظام التشغيل (OS) ، والميزات ، وإدارة تدفق المعلومات ، وأمان اللغة ، وبروتوكولات الشبكة ، وأمان الأجهزة ، وأمان تطبيقات الويب.
المحاضرة 1: "مقدمة: نماذج التهديد"
الجزء 1 /
الجزء 2 /
الجزء 3المحاضرة 2: "السيطرة على هجمات القراصنة"
الجزء 1 /
الجزء 2 /
الجزء 3 لذا ، لدينا عازلة نضع عليها "الكناري". وفوق ذلك ، يتم حفظ قيمة
EBP المحفوظة في مؤشر نقطة التوقف ويتم وضع عنوان الإرجاع فوقه. إذا كنت تتذكر ، فإن الفائض ينتقل من الأسفل إلى الأعلى ، لذا قبل الوصول إلى عنوان المرسل ، فسوف يدمر أولاً "الكناري".
الجمهور: لماذا سيؤثر على الكناري؟
البروفيسور: لأنه من المفترض أن المهاجم لا يعرف كيفية "القفز" في الذاكرة بشكل تعسفي. تبدأ هجمات تجاوز الذاكرة التقليدية مع هاكر يقوم بفحص حد حجم المخزن المؤقت ، وبعد ذلك يبدأ تجاوز الحد من الخط السفلي. ولكنك على حق - إذا تمكن المهاجم من الدخول مباشرة إلى شريط عنوان المرسل ، فلن يساعدنا "الكناري". ومع ذلك ، مع هجوم تجاوز سعة المخزن المؤقت التقليدي ، يجب أن يحدث كل شيء بهذه الطريقة تمامًا - من الأسفل إلى الأعلى.
وبالتالي ، فإن الفكرة الرئيسية لاستخدام "الكناري" هي أننا نسمح لاستغلال ضار لتجاوز ذاكرة التخزين المؤقت. لدينا رمز وقت التشغيل ، عند الرجوع من دالة ، يتحقق من "الكناري" للتأكد من أنه يحتوي على القيمة الصحيحة.
الجمهور: هل يمكن للمهاجم إعادة كتابة عنوان المرسل وتغيير "الكناري"؟ كيف يمكنه التحقق من أنه تم تعديله ، لكنه يواصل أداء وظيفته؟
الأستاذ: نعم ، ربما. وبالتالي ، يجب أن يكون لديك جزء من التعليمات البرمجية الذي سيتحقق من ذلك بالفعل قبل إرجاع الدالة. أي ، في هذه الحالة ، من الضروري الحصول على دعم مترجم ، والذي سيوسع فعليًا
اصطلاح استدعاء اصطلاح الاستدعاء . لذلك يحدث هذا الجزء من تسلسل الإرجاع قبل أن نفكر في صحة هذه القيمة للتأكد من أن "الكناري" لم يتم تدميره. فقط بعد ذلك يمكننا التفكير في شيء آخر.
الجمهور: ألا يستطيع المهاجم أن يعرف أو يخمن ما يعنيه "الكناري"؟
البروفيسور: هذا بالضبط ما سأتحدث عنه! ما هي مشكلة هذه الدائرة؟ ماذا لو ، على سبيل المثال ، وضعنا القيمة A في كل برنامج؟ أو فرع كامل من 4 قيم A؟ بالطبع ، يمكن لأي مخترق معرفة حجم المخزن المؤقت ، وسعته ، وبالتالي تحديد موقع "الكناري" في أي نظام. لذلك ، يمكننا استخدام أنواع مختلفة من الكميات التي نضعها في "الكناري" لدينا لمنع ذلك.
هناك شيء واحد يمكنك القيام به مع "الكناري". سيكون نوعًا مضحكًا جدًا من "الكناري" ، والذي يستخدم وظائف برنامج C ويعالج أحرفًا خاصة ، ما يسمى النوع الحتمية من "الكناري".

تخيل أنك استخدمت الحرف 0 لـ "الكناري" ، فالقيمة الثنائية للصفر هي صفر بايت ، والحرف صفر في ASCII. وتعني القيمة -1 العودة إلى الموضع السابق وهكذا. تتوقف العديد من الوظائف أو تغير العملية عندما تصادف أحرفًا أو قيمًا مثل 0 ، CR ، LF ، -1. تخيل أنك ، بصفتك مخترقًا ، تستخدم بعض وظائف إدارة السلسلة للصعود إلى المخزن المؤقت ، وتواجه الحرف 0 في "الكناري" وتتوقف العملية! إذا كنت تستخدم وظيفة "حرف الإرجاع" -1 ، والتي يتم استخدامها غالبًا كعلامة نهاية خط ، ستتوقف العملية أيضًا. لذلك -1 علامة سحرية أخرى.
هناك شيء آخر يمكن استخدامه في "الكناري" - وهي قيم عشوائية يصعب تخمينها بالنسبة للمهاجم. تعتمد قوة القيمة العشوائية على مدى صعوبة تخمينها من قبل المهاجم. على سبيل المثال ، إذا أدرك أحد المهاجمين أن هناك 3 بت فقط من الإنتروبيا في نظامك ، فسيكون قادرًا على استخدام هجوم القوة الغاشمة. لذلك ، فإن إمكانيات استخدام أرقام عشوائية للحماية من الهجمات محدودة للغاية.
الجمهور: يحدث عادةً أن أقرأ من مخزن مؤقت آخر وأكتب ما قرأته في هذا المخزن المؤقت لهذا المكدس. في هذه الحالة ، يبدو أن القيمة العشوائية لـ "الكناري" عديمة الفائدة ، لأنني قرأت البيانات من مخزن مؤقت آخر وأعرف أين يوجد "الكناري". لدي مخزن مؤقت آخر أتحكم فيه ولا أتحقق منه أبدًا. وفي هذا المخزن المؤقت يمكنني أن أضع الكثير مما أريد أن أضعه. لا أحتاج إلى "كناري" عشوائي ، لأنني أستطيع إعادة كتابته بأمان. لذلك لا أرى كيف يعمل حقًا - في السيناريو الذي اقترحته ، عندما تتوقف الوظيفة عند قراءة البيانات من المخزن المؤقت.
البروفيسور: أفهم سؤالك - تقصد أننا نستخدم "الكناري" القطعي ، ولكن لا تستخدم إحدى وظائف المكتبة القياسية التي يمكن "خداعها" من قبل شخصياتنا 0 ، CR ، LF ، -1. ثم نعم ، في الحالة التي وصفتها ، ليست هناك حاجة إلى "الكناري".
الفكرة هي أنه يمكنك ملء هذا المخزن المؤقت بالبايت من أي مكان ، ولكن أي شيء يسمح لك بتخمين هذه القيم أو الحصول عليها بشكل عشوائي سيؤدي إلى الفشل.
الجمهور: هل من الممكن استخدام شيء مثل عدد الثواني أو المللي ثانية كأرقام عشوائية واستخدامها في "الكناري"؟
البروفيسور: لا تحتوي مكالمات البيانات على العديد من الحوادث كما تعتقد. لأن البرنامج يحتوي على سجلات أو وظيفة يمكنك الاتصال بها لمعرفة متى تم تنزيل البرنامج ، وأشياء أخرى مماثلة. ولكن بشكل عام ، أنت على حق - من الناحية العملية ، إذا كان بإمكانك استخدام جهاز ، عادة ما يكون بمستوى منخفض ، مع توقيتات نظام أفضل ، فقد يعمل هذا النوع من النهج.
الجمهور: حتى إذا تمكنا من عرض السجلات حول بداية تجاوز سعة المخزن المؤقت ، فلا يزال من المهم في أي وقت نرفض الطلب. وإذا لم نتمكن من التحكم في المدة التي يستغرقها طلب جهاز الكمبيوتر إلى الخادم ، فمن المشكوك فيه أنه يمكن تخمين الوقت المحدد بشكل محدد.
البروفيسور: صحيح تمامًا ، سبق أن قلت أن الشر يكمن في التفاصيل ، هذه هي الحالة. بمعنى آخر ، إذا كان لديك طريقة ما ، على سبيل المثال ، لتحديد نوع قناة التوقيت ، فقد تجد أن مقدار الإنتروبيا أو عدد العشوائية لا يملأ طابعًا زمنيًا كاملاً ، ولكن أقل بكثير. لذلك ، يمكن للمهاجم تحديد الساعة والدقيقة عندما قمت بذلك ، ولكن ليس الثانية.
الجمهور: للتسجيل ، محاولة تقليص عشوائيتك فكرة سيئة؟
الأستاذ: صحيح تماما!
الجمهور: أي أنه يتعين علينا عادةً استخدام كل ما تدعمه أنظمتنا ، أليس كذلك؟
أستاذ: نعم هذا صحيح. يشبه هذا اختراع نظام التشفير الخاص بنا ، وهو شيء شائع آخر يريد خريجونا أحيانًا القيام به. لكننا لسنا وكالة الأمن القومي ، ولسنا علماء رياضيات ، لذلك عادة ما يفشل هذا. لذلك أنت على حق تماما في ذلك.
ولكن حتى إذا كنت تستخدم عشوائية النظام ، فلا يزال بإمكانك الحصول على عدد أقل من الإنتروبيا مما تتوقع. اسمحوا لي أن أقدم لكم مثالاً على التوزيع العشوائي للعناوين. على هذا المبدأ
يعمل نهج
المكدس الكناري . نظرًا لأننا منخرطون في أمان الكمبيوتر ، فمن المحتمل أنك تتساءل في أي الحالات لا يستطيع "الكناريون" التعامل مع مهمتهم وما إذا كانت هناك طرق لفشل "الكناري".
إحدى هذه الطرق هي الهجوم بإعادة كتابة مؤشرات الدالة. لأنه إذا ضربت ضربة على مؤشر الوظيفة ، فإن "الكناري" لا يمكنه فعل أي شيء.
لنفترض أن لديك رمزًا للنموذج
int * ptr ..... .. ، مؤشر البدء ، لا يهم كيف ، ثم يكون لديك المخزن المؤقت
char buf [128] ، ووظيفة
get (buf) ، وفي الأسفل يوجد مؤشر تم تعيينه بعض القيمة :
* ptr = 5 .
ألاحظ أننا لم نحاول مهاجمة عنوان الإرجاع للدالة التي تحتوي على هذا الرمز. كما ترى ، عندما يفيض المخزن المؤقت ، سيتلف عنوان المؤشر الموجود فوقه. إذا تمكن المهاجم من إتلاف هذا المؤشر ، فيمكنه حينئذٍ تعيين 5 إلى أحد العناوين التي يتحكم فيها. هل يستطيع الجميع أن يروا أن "الكناري" لن يساعد هنا؟ لأننا لا نهاجم المسار الذي تعود فيه الدالة.
الجمهور: هل يمكن وضع المؤشر أسفل المخزن المؤقت؟
البروفيسور: يمكن
ذلك ، ولكن ترتيب متغيرات محددة يعتمد على العديد من الأشياء المختلفة ، على طريقة ترتيب المترجم للمحتوى ، وعلى حجم عمود الأجهزة ، وما إلى ذلك. ولكنك على حق ، إذا ارتفع تجاوز سعة المخزن المؤقت ، وكان المؤشر يقع أسفل المخزن المؤقت ، فلن يتمكن الفائض من إتلافه.
الجمهور: لماذا لا يمكنك ربط "الكناري" بوظيفة "الكناري" ، كما فعلت مع عنوان المرسل؟
الأستاذ: هذه لحظة مثيرة للاهتمام! يمكنك أن تفعل مثل هذه الأشياء. في الواقع ، يمكنك محاولة تخيل مترجم أنه كلما كان لديه مؤشر ، فإنه يحاول دائمًا إضافة وظيفة إضافية لبعض الأشياء. ومع ذلك ، فإن فحص كل هذه الأشياء سيكون مكلفًا للغاية. لأنه في كل مرة تريد استخدام أي مؤشر أو استدعاء أي وظيفة ، يجب أن يكون لديك رمز للتحقق مما إذا كان هذا "الكناري" صحيحًا. في الأساس ، يمكنك القيام بشيء مماثل ، ولكن هل هذا منطقي؟ نرى أن "الكناري" لا تساعد في هذه الحالة.
وهناك شيء آخر ناقشناه سابقًا هو أنه إذا تمكن المهاجم من تخمين العشوائية ، فلن تعمل "الكناري" العشوائية من حيث المبدأ. يعد إنشاء موارد الأمان استنادًا إلى العشوائية موضوعًا معقدًا ومعقدًا للغاية ، لذلك لن نتطرق إليه.
الجمهور: هل يحتوي الكناري على بتات أقل من عنوان المرسل؟ لأنه بخلاف ذلك ، لا يمكنك فقط تذكر هذا العنوان والتحقق مما إذا كان قد تغير؟
الأستاذ: دعنا نرى. أنت تتحدث عن هذا المخطط عندما يكون "الكناري" أعلى المخزن المؤقت ، وتعني أن النظام لا يمكن أن يكون آمنًا إذا كان من المستحيل النظر إلى عنوان الإرجاع والتحقق مما إذا كان قد تم تغييره.

نعم ولا. يرجى ملاحظة أنه في حالة حدوث هجوم تجاوز سعة المخزن المؤقت ، سيتم الكتابة فوق أي شيء فوقه ، لذلك يمكن أن يسبب هذا مشاكل. ولكن في الأساس ، إذا كانت هذه الأشياء غير قابلة للتغيير بطريقة ما ، فيمكنك فعل شيء كهذا. لكن المشكلة هي أنه في كثير من الحالات ، يعد التلاعب بعنوان العودة أمرًا معقدًا إلى حد ما. لأنه يمكنك أن تتخيل أنه يمكن استدعاء وظيفة خاصة من أماكن مختلفة ، وهكذا. في هذه الحالة ، نركض قليلاً إلى الأمام ، وإذا كان هناك وقت في نهاية المحاضرة ، فسوف نعود إلى ذلك.
هذه هي الحالات التي قد يفشل فيها "الكناري". هناك أماكن أخرى حيث الفشل ممكن ، على سبيل المثال ، عند مهاجمة
malloc ووظائف
حرة . تقوم الدالة malloc بتخصيص كتلة من الذاكرة بحجم معين بالبايت وإرجاع المؤشر إلى بداية الكتلة. لم يتم تهيئة محتويات كتلة الذاكرة المخصصة ، فهي تظل بقيم غير محددة. وتعمل الوظيفة
المجانية على تحرير الذاكرة المخصصة مسبقًا بشكل ديناميكي.
هذا هجوم فريد من نوعه في أسلوب C. دعونا نرى ما يحدث هنا. تخيل أن لديك مؤشرين هنا p و q نستخدم
malloc من أجل تخصيص 1.024 بايت من الذاكرة لكل من هذه المؤشرات. لنفترض أننا نجعل الدالة
strcpy من أجل p من نوع من خطأ العازلة الذي يتحكم فيه المهاجم. هذا حيث يحدث الفائض. ثم نقوم بتشغيل الأمر
free q و
free p . هذا رمز بسيط جدا ، أليس كذلك؟

لدينا مؤشرين تم تخصيص ذاكرة لهما ، ونستخدم أحدهما لدالة معينة ، ويحدث تجاوز سعة للمخزن المؤقت ، ونحرر ذاكرة كلا المؤشرين.
افترض أن خطوط الذاكرة لكل من p و q تقع بجانب بعضها البعض في مساحة الذاكرة. في هذه الحالة ، يمكن أن تحدث أشياء سيئة ، أليس كذلك؟ لأنه يتم استخدام الدالة
strcpy لنسخ محتويات
str2 إلى
str1 . يجب أن يكون
Str2 مؤشرًا لسلسلة تنتهي بصفر ، ويعيد
strcpy مؤشرًا إلى
str1 . إذا تداخل الخطان
str1 و
str2 ، فإن سلوك الدالة
strcpy يكون غير محدد.
لذلك ، يمكن أن
تؤثر وظيفة
strycpy التي تعالج الذاكرة
p في نفس الوقت على الذاكرة المخصصة لـ
q . ويمكن أن يسبب مشاكل.
من المحتمل أنك فعلت شيئًا كهذا في التعليمات البرمجية الخاصة بك عن غير قصد عندما استخدمت نوعًا من نوع غريب من المؤشرات. ويبدو أن كل شيء يعمل ، ولكن عندما تحتاج إلى استدعاء الوظيفة
المجانية ، يحدث مثل هذا الإزعاج. ويمكن للمهاجم الاستفادة من ذلك ، سأشرح لماذا يحدث هذا.
تخيل أنه داخل تنفيذ وظائف
free و
malloc ، تبدو الكتلة المميزة مثل هذا.
لنفترض أنه في الجزء العلوي من الكتلة توجد بيانات تطبيق مرئية ، وأدناه لدينا حجم المتغير. هذا الحجم ليس ما يراه التطبيق مباشرة ، بل هو نوع من "المحاسبة" التي يتم إجراؤها بواسطة
free أو
malloc ، بحيث يمكنك معرفة حجم ذاكرة التخزين المؤقت المخصصة. توجد كتلة مجانية بجوار الكتلة المحددة. لنفترض أن الكتلة الحرة تحتوي على بعض البيانات الوصفية التي تبدو مثل هذا: لدينا حجم الكتلة أعلاه ، وهناك مساحة حرة تحته ، والمؤشر الخلفي والمؤشر الأمامي تحته. وفي الجزء السفلي من الكتلة ، يظهر الحجم مرة أخرى.

لماذا لدينا مؤشرين هنا؟ لأن نظام تخصيص الذاكرة في هذه الحالة يستخدم قائمة مرتبطة بشكل مزدوج لتتبع كيفية ارتباط الكتل الحرة ببعضها البعض. لذلك ، عند تحديد كتلة مجانية ، فإنك تستبعدها من هذه القائمة المرتبطة بشكل مضاعف. وبعد ذلك ، عند تحريره ، ستقوم ببعض الحساب للمؤشر وترتيب هذه الأشياء. بعد ذلك ، يمكنك إضافته إلى هذه القائمة المرتبطة ، أليس كذلك؟
كلما سمعت عن الحساب المؤشر ، يجب أن تعتقد أن هذا هو "الكناري" الخاص بك. لأنه سيكون هناك الكثير من المشاكل. اسمحوا لي أن أذكركم بأننا لدينا تجاوز سعة المخزن المؤقت. إذا افترضنا أن
p و
q بجوار بعضهما البعض ، أو قريبان جدًا من مساحة الذاكرة ، فقد يحدث في النهاية أن تجاوز سعة هذا المخزن المؤقت يمكنه الكتابة فوق بعض بيانات الحجم للمؤشر المخصص
q - هذا هو الجزء السفلي من الكتلة المخصصة لدينا. إذا واصلت متابعة أفكاري منذ البداية ، فإن خيالك سيخبرك أين يبدأ كل شيء في الخطأ. في الواقع ، ما يحدث في نهاية المطاف مع هذه العمليات هو
q و
p مجانًا - فهم ينظرون إلى هذه البيانات الوصفية في الكتلة المحددة للقيام بكل التلاعبات الضرورية بالمؤشر.

أي أنه في مرحلة ما من التنفيذ ، ستحصل الوظائف
المجانية على مؤشر معين بناءً على قيمة الحجم:
p = get.free.block (الحجم) ، والحجم هو ما يتحكم به المهاجم ، لأنه قام بتجاوز سعة المخزن المؤقت ، بشكل صحيح ؟؟؟
لقد قام بمجموعة من الحسابات الحسابية ، ونظر
إلى وظيفة
الخلف ومؤشرات هذه الكتلة ، وسيفعل الآن شيئًا مثل تحديث المؤشرات "الخلفية" و "الأمامية" - وهما الخطان السفليان.

ولكن في الواقع هذا لا ينبغي أن يزعجك. هذا مجرد مثال للرمز الذي يحدث في هذه الحالة. ولكن في الحقيقة ، نظرًا للحجم الذي أعاد القرصان كتابته ، فإنه يتحكم الآن في هذا المؤشر ، الذي يمر عبر الوظيفة
المجانية . وبسبب هذا ، فإن الحالتين هنا في الخلاصة هي في الواقع تحديثات للمؤشر. وبما أن المهاجم كان قادرًا على التحكم في هذه
النقطة ، فإنه يتحكم في الواقع في هاتين المؤشرين. في هذا المكان يمكن أن يحدث هجوم.
لذلك ، عند العمل
بحرية ومحاولة القيام بشيء مثل دمج هذين الكتلتين ، يكون لديك قائمة مرتبطة بشكل مضاعف. لأنه إذا كان لديك مجموعتان تتصادمان مع بعضهما البعض وكلتاهما حرة ، فأنت تريد دمجهما في كتلة واحدة كبيرة.
ولكن إذا تحكمنا في الحجم ، فهذا يعني أننا نتحكم في العملية بأكملها من الأسطر الأربعة أعلاه. هذا يعني أنه إذا فهمنا كيفية عمل تجاوز السعة ، فيمكننا كتابة البيانات في الذاكرة بالطريقة التي نختارها. كما قلت ، تحدث مثل هذه الأشياء غالبًا مع التعليمات البرمجية الخاصة بك ، إذا لم تكن ذكيًا بمؤشر. عند ارتكاب بعض الأخطاء المجانية المزدوجة مثل
q المجانية و
p المجانية أو أي شيء آخر ، فإن تعطل وظيفتك. لأنك أخطأت في استخدام البيانات الوصفية التي تعيش في كل من هذه الكتل المحددة ، وفي مرحلة ما ستشير هذه العملية الحسابية إلى نوع من قيمة "القمامة" ، وبعد ذلك ستكون "ميتًا". ولكن إذا كنت مهاجمًا ، فيمكنك اختيار هذه القيمة واستخدامها لصالحك.
دعنا ننتقل إلى نهج آخر لمنع هجمات تجاوز سعة المخزن المؤقت. هذا النهج للتحقق من الحدود. الغرض من فحص الحدود هو التأكد من أنه عند استخدام مؤشر معين ، فإنه لن يشير إلا إلى كائن ذاكرة. وهذا المؤشر يقع ضمن الحدود المسموح بها لكائن الذاكرة هذا. هذه هي الفكرة الرئيسية للتحقق. — . , C, . , : , , ?
, – . 1024 , :
char [1024] ,
char *y = & [108].
? ? من الصعب القول. , , . , , - .
- , , , . . , , , . , , . , , .
, ,
struct union . , . :
integer ,
struct ,
int .
,
union , . ,
integer ,
struct , .
, , - :
int p: & (u,s,k) , : u, s, k.

, , , , . , ,
union integer ,
struct . , , , . .
p' ,
p ,
p' , .

, , . , ,
union . , - -
union , , , . , , X. , , , , . , , . .
, . ,
p p' , . .
? Electric fencing – . , , , , .

, - , . , , . , . , , , .
- C C++, , , . - , , - . , . , «» — , , , . , , .
, guard page – ! , .
59:00
:
MIT « ». 2: « », 2النسخة الكاملة من الدورة متاحة
هنا .
, . ? ? ,
30% entry-level , : VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps $20 ? ( RAID1 RAID10, 24 40GB DDR4).
Dell R730xd 2 ? 2 Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100 $249 ! . c Dell R730xd 5-2650 v4 9000 ?