مقال : نبذه قصيره عن Buffer Overflow
السلام عليكم
ان شاء الله هذا يعتبر موضوعى الاول فى مجتمع iSecur1ty سوف أتكلم عن موضوع شيق ولاحظت أنه لا يشغل أحد فى الوطن العربى الا قله قليله من أبنائيه لذلك قررت التحدث عن ما يسمى Buffer OverFlow.
كثير من المبرمجين يقعون فى بعض الاخطاء مثل مبرمج ال php يحدث وان يقع فى خطأ ما يسمى بال SQL injection فيؤدى ذلك الى حدوث خسائر كبيره وهذا مثال بسيط للأخطاء البرمجيه من هنا نتحدث عن موضوعنا وهوا أيضا خطأ برمجى من قبل مبرمج ليس من الضرورى أن يكون غير ماهر ولكن كثير من المبرمجيين يقع فى هذه الاخطاء ولولا ذلك ما كان exploit-db موجودا.
لفهم الموضوع جيدا لابد من النزول الى بعض التفاصيل من ضمن هذه التفاصيل كان لابد التعرف على ال Stack و كيفيه عمله.
تعريف ال Stack:
هو جزء متصل من الذاكره يستخدم غالبا بواسطه ما نسميه بال functions لكى يتم حفظ arguments الخاصه بها وأيضا لعمل ما يسمى dynamically allocate space for local variables.
التعامل مع ال Stack:
للتعامل مع ال stack نتعامل معه بأمريين هما (push, pop). يستخدم الامر push: لتخزين شئ معين فيه كما يستخدم الامر pop: للحصول على قيمه منه .
طريقه عمل ال Stack:
يعمل بطريقه تسمى (LIFO) وهوا ما يعنى ب Last input First output أى ما يخزن فيه الاخير يتم سحبه الأول و هذا شكل تصورى له.
من أجل هذه العمليه كان لابد من تنظيم الامر فوجد ما يسمى بالمخزن أو ما يسمى ب Register خاص بال stack يسمى ESP وهو
stack pointer يشير الى قمه ال stack يتغير بعد كل عمليه تحدث فيه.
كيفيه عمل ال stack مع ال functions:
عندما يتم أستدعاء ال function يتم عمل عمليه push لل arguments الخاصه بها فى ال stack بعدها تتم نفس العمليه أيضا لما يسمى ب IP وهوا أختصار ل instruction pointer وهوا عباره عن عنوان يتم تسجيله من أجل أذا ما انتهت ال function من عملها يتم العوده الى هذا العنوان و تكمله سير البرنامج يخزن أيضا ما يسمى بالقيمه القديمه لل EBP و ال local variables الخاصه بال function.
تعرفنا على العلاقه القويه بين ال function و stack و عرفنا كيفيه أعطاء ال function ال arguments من خلال ال stack.
تعريف على Stack Buffer Overflows:
واحد من أشهر أنواع ال vulnerabilities الموجوده يستغل فى حاله ما أذا كانت ال function تأخذ قيم من المستخدم وتقوم بوضع هذه القيمه فى متغير ما أو مكان معين فى الذاكره وهوا ما يطلق عليه فى البرمجه buffer أما ال overflow يأتى من أن هذه ال function لا تقوم بعمليه فحص لحجم هذا المدخل ومقارنته بحجم ال buffer المخصص لها.
مثال بسيط:
شرح البرنامج:
هذا البرنامج كل ما عليه ان يأخذ معطى من المستخدم ثم طبعه على الشاشه مره أخرى وهذا يتم من خلال أثنين من ال functions الخاصه بالسى وهما gets, puts .
تقوم gets بأخذ مدخل من المستخدم و وضعه فى buffer معين يتم تحديده لها. أما عن puts تأخذ buffer وتقوم بطباعته على الشاشه.
ومن هنا تتحقق شروط ال buffer overflow وهي وجود function تتعامل مع ال stack وتأخذ arguments تأتى من المستخدم وهذه ال function لا تقوم بفحص للحجم.
نجد أيضا أنه يوجد عدد 2 function فى البرنامج واحده تستخدم من قيل ال main والاخرى لا يتم أستخدامها .
نقوم الان ب عمل compile للبرنامج كما هوا موضح:
هنا و أنا بقوم بهذه العمليه يوجد فى نظام تشغيل linux ما يسمى ب Address Space Randomization وهو يعطى عنوان عشوائى للبرنامج عند تنفيذه ولكن هنا قمنا بالسيطره على هذا السيناريو للشرح حتى أذا قمنا بتشغيل البرنامج مره أخرى بكون بنفس العناويين كما نقوم بألغاء الحمايه الداخليه لل stack وأيضا نفعل خاصيه الinternal debuger من أجل فحص البرنامج جزء جزء وهذه بعض الصور من البرنامج حين يعمل: حيث أقوم بعمل test لل buffer overflow
هنا قمنا بتجربه البرنامج أكثر من مره مع زياده حجم المدخل الذى يؤخد بواسطه gets ويخزن فى buffer ولكن هذا المخزن حجمه 8-bytes وحجم المدخل زادت عن هذا الحجم ومع الزياده ظهر ما يسمى ب segmentation fault وهذه رساله خطأ تحدث فى حالات معينه منها عند محاوله الوصول لعنون غير صحيح فى الذاكره ولمعرفه ماذا حدث لابد من عمليه تسمى ال debuging تسخدم فى ذلك أدوات كثيره أشهرها gdb
ثم نأخد المدخل الذى ظهر عنده الخطأ وندخله من خلال ال gdb لفحص محتوى ال stack عند هذه الحاله ومعرفه سبب حدوث الخطأ.
هنا ما حدث انه أخذ ال 8-bytes الخاصيين بال local buffer ثم أخذ تخطى ال old value for BP ثم بدأ بالكتابه فوق ال Return address وهذا العنوان هوا قيمه الIP المسجل فى ال stack من أجل الذهاب له بعد الانتهاء من الfunction فعند الذهاب يأتى أن يأخذ العنوان يجده عنوان لا وجود له وهذا هوا السبب فى ظهور الخطأ.
عند فتح البرنامج بال gdb يظهر كما هوا موضح بالصوره:
نقوم بعمل break point عند كل ال functions الموجوده عندى فى البرنامج ثم نقوم بعمل run للبرنامج بعمل ما يسمى ب disassemble لل main function لملاحظه العنوايين كما هوا موضح:
الان البرنامج يتوقق عند أول سطر فى ال function normall لتنفيذها مع الفحص next ومن هنا نقوم بفحص ال stack ونقارن العناويين لمعرفه ال return address المخزن فى ال satck. ماذا تتوقع ان ترى فى هذا العنوان؟! لابد أن ترى ال address الذى يذهب أليه البرنامج بعد الانتهاء من تنفيذ ال function normall وهوا 0x08048486 مع التدقيق فى بيانات ال stack سوف ترى هذا العنوان موجودا به.
عند الانتقال الى الأمر التالى بأمر next سوف نصل للهدف وهوا أخذ البرنامج المدخل الخاص بيا. البرنامج أظهر الخطأ عند ال byte رقم ١٢ سوف أعطى أول 8-bytes حرف A و ال 4-bytes التاليه حرف B أما ال 4-bytes الاخيره حرف C لكى يصبح المدخل هكذا AAAAAAAABBBBCCCC
نعيد فحص ال stack ثانيه بعد هذه العمليه نجد أن قيمه ال return address تغيرت و أصبحت قيمتها 0x43434343 حيث أن ال ٤٣ هيا قيمه ال capital C بالأسكى وهذا دليل على مقدرتى على تغيير هذا العنوان.
ال function hacked غير مستخدمه فى البرنامج أطلاقا ولكن أذا حصلنا على عنوان هذه ال function فى الذاكره يكون من السهل اعطاء العنوان لل return address وبدل ما يسرى البرنامج فى طريقه الصحيح والمفترض يأخذ طريق أخر وهوا تنفيذ هذه ال function الغير مستخدمه فى main function ولكن كيف يمكن الحصول على عنوان هذه ال function؟ الجواب عن طريق disassemble function كما هوا موضح:
أخذ أول عنوان فى العناويين الموجوده و هو بدايه تنفيذ ال hacked function وهو 0x0804844c يمكننا الان الخروج من ال gdb لقد قمنا بالغرض الكامل منه.
هقوم الان بتشغيل البرنامج من خلال أعطائه المدخلات من خلال أداه printf التى يمكنها أعطاء قيم hex من خلال \x ولكن سوف هعطى العنوان بطريقه عكسيه نظرا لطريقه عمل ال stack كما شرحتا من قبل :
كما رأينا تم تنفيذ ال hacked function من قبل البرنامج بمجرد أعطائها عنوان ال function ومن هذا المثال رأينا نبذه عن ال buffer overflow وكيفيه التعامل معه.
ليس قليلا ما ذكر ولكن الذى ذكر فى المثال لمجرد التوضيح وليس البرنامج السابق صغير فهو جزء من application كبير و مشهور مثلا. وبدلا من أعطاء البرنامج المدخلات من خلال نفس الجهاز ممكن أن يأخذ مدخلاته من خلال port وبدلا أيضا من أجبار البرنامج على تنفيذ ال function صغيره كما فى المثال من الممكن ان اجبرها على تنفيذ كود خبيث موضوع عند عنوان معين فى الذاكره وأجبر البرنامج على تنفيذه.
أتمنى أن يكون قد نال أعجابكم….
ثغرات bof معروفة جدا لخطورتها على انظمة تشغيل وبرامجها
انا سابق كنت اكتشفها واضعها في مواقع Milworm الشهير ، ايام زمان 🙂 شكرا على تدكير
تسلم على الشرح الله يبارك فيك
تسلم يا معلم ضياء على المقال شرح رائع جدا بارك الله
ما شاء الله درس مفصل ومجمل لك كل التقدير
شكرا لك اخي الكريم على الشرح الاكثر من رائع
ربما الامر يحتاج الى خبرة في الاسملبي و RE + لاسف لا تكتشف في لغات كدلفي و vb.net ,,,
انما في سي و ++
من أجمل المواضيع اللتي قرأتها من ناحية الترتيب والتنسيق المدعم بالصور، و أعجبني أكثر بأنك شرحت موضوع لم يهتم له الهكرز العرب كثيراً، بالتوفيق لك.
شكرا شباب الحمد لله ان المقال نال أعجابكم
موضوع جميل انا حاولت اجربة اكثر من مرة لكن كنت اخطئ فى كل مرة لكن لاحظت شى معين و هو عن كتابة هذا الكود : ^[[A ثلاثة مرات يتم رفع مؤشر الكتابة سطر الى اعلى و هو الكود الذى يكتب عند الضغط على الزر الذى يشير الى اعلا
لكن لوسمحت انا متدأ فى لغة الاسمبلى فبماذا تنصحنى حتى اصبح افضل فى هذة اللغة
لو ممكن توضح أكتر عشان أعرف أساعدك عشان أنا بس مش فاهم حاجه؟
لو عاوزنى أعمل معاك الموضوع ممكن تكلمنى على الميل
لا بس لو ينفع تقولى على مصادر او كتب سهلة لابدا فى الاسمبلى و الهندسة العكسية و لو فى كتب او مواضيع عربى لتعلم ذلك بسهولة لافهم هذة اللغة بسهولة لاغراض اختبار الاختراق لانى لا اعرف من اى ابدا بالضبط و شكرا
أنصحك بمتابعه كتاب Shell Coder Hand Book دا ان شاء الله هيظبط معاك جامد أوووى ولو عاوز حاجه عربى بقى مفيش غير كورس تبع الشركه عن ال exploitation هيعلن عنو قريبا ان شاء الله
شكرا جدا جدا و لو فى كتب تانية بالانجليزى و شكرا جدا الف شكر
يعطيك العافية اخ ضياء
شرح مختصر مفيد ورائع
very nice thanks
كيف نقوم بضبط Address Space Randomization وكذلك الغاء الحماية عن المكدس في ubuntu
عن طريق أعطاء الكومبايلر ما يسمى arguments
-fno-stack-protector
-mpreferred-stack-boundary=2
كما هو موضح فى الصوره أخى ولو لديك مشاكل أخرى ممكن أساعدك فيها وشكرا على محاولتك تنفيذ الشرح
أخي ضياء شكرا جزيلا لك…في الحقيقة جربت الشرح بالتفصيل والحمد لله تم توضيح الفكرة كاملة…لكن لدي بعض الاستفسارات:
1 ـ حسب علمي أن تعليمة الحجز char تقوم بحجز مصفوفة من البايتات حسب العدد المدخل للمتحول buffer …وفي حالتنا القيمة هي 8 …السؤال لماذا لم يظهر الخطأ عند إدخال قيمة للمتحول تتجاوز 8 بايت(AAAAAAAABBBB)….حسب علمي أن الحاسب يحجز بايت لكل حرف …..فلماذا أدخلنا 12 بايت حتى ظهر الخطأ؟؟
2 ـ الحالة الفعلية للاستغلال هي كتابة برنامج استغﻻل يحوي كود خبيث بمعزل عن البرنامج المصاب الذي لم نرى كوده وﻻ نعرف عن برمجته الداخلية شيء…لذلك قمت بكتابة برنامج آخر يحتوي فقط على الوظيفة hacked..بعد الترجمة والتنقيح بـgdb جلبت عنوانها…وتوقفت قبل أن أنهي البرنامج للتأكد من أنه مازال محملا في الذاكرة… ومن ثم فتحت نافذة terminal أخرى وقمت بتشغيل البرنامج الرئيسي المصاب بالثغرة وحاولت أن أمرر عنوان الـ hacked لكي يتم تنفيذها لكني فشلت..ﻻ أدري هل يمكن وضع عنوان والقفز إليه من خارج البرنامج الاساسي المصاب؟؟أم أن الكود الخبيث يجب أن يكون ضمن برمجة البرنامج الاساسي المصاب حصرا؟؟؟؟
شكرا جزيلا وسلفا أخي الكريم على كل ما قدمته…أتمنى أن أرى لك موضوعا آخر تستكمل من خﻻله ما بدأته وما أشرت إليه في سطورك الأخيرة حول إمكانية تمرير المدخلات عن طريق الـ ports
المعطى الزائد هذا يسمى بال padding chars فأنا ب أقوم بتغيره هوا أيضا والكتابه عليه, والكود الخبيث يوضع فى نفس البرنامج وليس فى برنامج أخر وهذا العلم ما يعرف باسم Shell Code ان شاء الله سوف أتكلم عليه فى القريب العاجل ولكن أعزرنى أخى بسبب الدراسه والامور المشابهه بس بجد انا فرحان ان فيه حد مهتم وبيتعلم من العرب وسوف أعطيك الاميل الخاص بيا لكى تتواصل معى من خلاله و بأذن الله لو قررت تمشى فى المجال دا بساعدك فيه
[email protected]
شكرا جزيلا اخي ضياء بانتظار جديدك…وسأضيف ايميلك لدي…شكرا على هذه اللفتة الطيبة منك…إنشاء الله سأستعين بك وبخبرتك في قادم الأيام…أتمنى لك امتحانات موفقة مع الأمل بعودة سريعة لنا مع مقاﻻت غنية
السلام عليكم ورحمة الله
بارك الله فيك أخي ضياء شرح رائع
صراحة استطعت أن أتابع الشرح في البداية لأنه عندي مستوى جيد بلغة البرمجة سي ولم أجد مشكل
في وسط الشرح تهت قليلا لأن مستواي بنظام اللينكس ليس بالرائع
أحب جدا أن أتواصل معك وأن أتابع مقالاتك القادمة بإذن الله
جزاك الله خيرا
مشكور الله يفتح عليك