البافر اوفر فلو: كتابة الشيل كود - before overflow

السلام عليكم ورحمة الله وبركاته



إن شاء الله تكونوا في تمام الصحة والعافية إخواني




كنا قد شفنا في الدرس السابق كيف نقوم بإستبدال قيمة RET داخل المكدس وذلك لالتحكم في EIP

وعندما تمكنا من التحكم في EIPجعلناها تشير إلى عنوان التعليمة في البرنامج التي نريد تنفيذها .

يعني بتحكمنا في المسجل EIP يمكننا أن نجعلها تشير إلى البايلود تبعنا و القابل للتنفيذ .


في هذا الشرح حنشوف الخطوات الأساسية لكتابة شيل كود بسيط قابل للتنفيذ وتخزينه في المكدس .


ماهو البايلود ؟

البايلود هو كود بلغة الآلة يتم تنفيذه من قبل المعالج مباشرة من دون أن يحتاج إلى الأسمبلر أو عملية compilation.

وهذا البايلود مايسمى بالشيلكود .

نستعمل هذا البايلود لإنتاج شيل بعد هجمات البافر أوفر فلو ... ومن هنا جائت تسميته بالشيل كود

لمذا سنكتب شيل كود ؟

نقوم بكتابة شيل كود وذلك لنجعل برنامج الهدف يشتغل بطريقة غير التي صمم من أجلها .

الطريقة الوحيدة لفعل ذلك هي إجبار البرنامج لعمل System Call أو نعبر عليه أيضا ب syscall
ال system call يسمح لنا بالوصول للكيرنل وذلك ليسمح

لنا الكيرنل بإستعمال الوضائف منخفضة المستوى مثل قرائة الملفات ()read

والكتابة فيها() write .. إلخ

يعني syscall هو واجهة بين وضع الكيرنيل المحمي (protected kernel mode)

ووضع المستخدم (user mode) في النظام .

كيف نقوم بكتابة الشيل كود ؟

توجد طريقتين لتنفيذ syscall في نظام linux :

إما نستعمل مكتبة libc بطريقة غير مباشرة .

وإما نقوم بتنفيذ syscall مباشرة بالأسمبلي وذلك بتحميل المسجلات ب arguments المناسبة ثم نستدعي التعليمة int 0x80 (software
interrupt) .

عندما يقوم برنامج في وضع مستخدم (user mode) بتنفيذ التعليمة int 0x80يتحول المعالج إلى وضع الكيرنل (kernel mode) ثم ينفذ الوضائف الموجودة في مكتبة أو واجهة syscall .

تتم هذه العملية كالتالي :

[1]تحميل رقم syscall المحدد في المسجل EAX

[2]يتم تحميل المسجلات بال arguments
المناسبة التي تحتاجها وظيفة في syscall.
مثلما شفنا في درس Hello World تنفيذ الوظيفة system call exit و write system call

[3] يتم تنفيذ التعليمة int 0x80

[4] يتحول المعالج إلى وضع الكيرنيل (kernel mode)

[5] يتم تنفيذ الوظيفة تبع system call



أبسط وظيفة في syscall هي exit()

و ما سنفعله في هذا الشرح هو كتابة شيل كود للوظيفة exit() تبع syscall

مهمة exit() هي إنهاء العملية الجاري تنفيذها .



ننتقل الآن إلى مراحل كتابة الشيل كود . [/COLOR]

[1] كتابة كود بلغة C ونجعله قابل للتنفيذ

نقوم بكتابة برنامج يقوم فقط عند تنفيذه بالمغادرة .

الكود كالتالي :

#include<stdlib.h>

main()
{
     exit(0);
}
وعند عملية compilation نستعمل الخاصية -static لمنع عملية الديناميك linker .

ثم :



كما لاحظتم إخواني فقط قام بالمغادرة .



المرحلة الثانية :

[2] نقوم بتحويل الكود المكتوب بلغة C إلى مايقابله من كود في لغة الأسمبلي

نقوم بذلك بفتح البرنامج مع GDB ثم كما في الصورة التالية :



نقوم بتحليل التعليمات :

وقلنا أن كلما نستخدم System Call هذه التعليمة int $0x80 تمثل Software interupt
وهي تدل على أنه تم إستدعاء System Call

نلاحظ عندما قمنا ب disas لل exit يوجد 2 software interrupt وهذا يعني أنه تم إستدعاء إثنين System Call

الأولى على هذا العنوان :

  0x0804f9c5
والثانية على هذا العنوان :

 0x0804f9cc
طيب إلى حد لآن عرفنا أنه تم إستخدام إثنين System Callتعالو نشوف الآن ماهما .



كنا قد ذكرنا أن System Call number يكون دائما

في المسجل EAX يعني ذلك أن من هذه التعليمة :

mov 0xfc, %eax
هذه التعليمة قامت بتحويل العدد 0xfc إلى المسجل EAX

هذا العدد مكتوب بالنظام السداسي العشري (hexdecimal) فنحوله للنظام العشري (decimal) يصبح 252

وهذا يعني أن System Call number يساوي 252 يعني تم إستدعاء الوظيفة تبع System Call التي تحمل الرقم 252 .

نفتح ترمينال جديد و نشوف ماهي الوظيفة التي تحمل الرقم 252 في System Call كما في الصورة التالية :



طيب إخواني كما لاحظتم في الصورة أن هذه الوظيفة هي exit_group .

طيب يعني في هذه الوظيفة exit()

تم إستدعاء الوظيفة الأولى تبع system call وهي exit_group

ثم تم إستدعاء الوظيفة الثانية تبع system call وهي exit

قلنا أنه يتم تحميل أول argument تبع وظيفة في syscall في المسجل EBX يعني هذه التعليمة :

 mov 0x4(%esp), %ebx
هذه تعني أننا قمنا بتحويل قيمة إلى المسجل EBX تم دفعها مسبقا في المكدس وكنا قد ذكرنا أن أول argument في syntax تبع exit()يمثل statut ونحوله في المسجل EBX يعني هذه القيمة هي 0
يعني 0 تبع (0)exit .




طيب الآن بعدما حللنا الكود وفهمنا مافيه ننتقل إلى المرحلة الثالثة


[3]حذف الأكواد الزائدة في الكود

طيب نحن الكود shell.c الذي كتبناه مهمته هو أنه عند تنفيذه يقوم بالمغادرة .

وقمنا قبل قليل بتحويل كود shell.c إلى الأسمبلي

ووجدنا أنه فيه إثنين system call وهما exitو exit_group

في هذه المرحلة حنحذف الأكواد الزائدة من الكود shell.c مع الحفاظ على مهمته .



حجم الشيل كود

يجب أن نجعل من الشيل كود صغير الحجم أقصى مايمكن . بقدر مايكون الشيل كود صغير بقدر مايكون أكثر فائدة .

تذكروا أننا سنقوم بتخزين الشيل كود داخل مكان مصاب في الذاكرة مخصص للمدخلات .
نقول مثلا حجم هذا المكان المصاب هو n بايت ,

فيجب علينا ملئ كل هذا المكان بالشيل كود يعني حجم الشيل كود تبعنا يجب أن يكون أصغر من n بايت .
ولهذا يجب علينا دائما تفكر الحجم .


نحن الآن لدينا في الشيل كود تبعنا 6 تعليمات بالأسمبلي .

ماهي مهمة الشيل كود تبعنا ؟
نحن نريد كتابة شيل كود للإستعمال exit syscall يعني مهمة الشيل كود تبعا هو فقط يقوم عند تنفيذه مع برنامج يقوم بإيقافه والمغادرة.

نأخذ التعليمة الأولى :

 mov 0x4(%esp), %ebx
قلنا هذه التعليمة خاصة بتخزين أول argument يحمل القيمة 0 في المسجل EBX

يعني يمكننا بكل بساطة القيام بتحميل المسجل EBX بالقيمة 0 يدويا وذلك بكتابة التعليمة التالية :

  movl $0, %ebx
إضافة إلى ذلك نحن نريد فقط كتابة شيل كود للوظيفة ()exit تبع syscall فيمكننا إزالة التعليمات تبع ()exit_group مادام ذلك لايؤثر على المهمة .

يعني التعليمات تبعنا في الأخير تصبح هكذا :

movl $0, %ebx
movl $1, %eax
int $0x80
الآن نكتب ذلك في برنامج بلغة الأسمبلي كالآتي :



الآن نقوم بعملية compilation ثم حنقوم بتحويل كود shell.s المكتوب بلغة الأسمبلي إلى opcodes((الأكواد التي يفهمها المعالج)) وعرض هذا الكود بشكل منظم وواضح للقرائة، حنستعمل في ذلك الأداة objdump كما في الصورة التالية :



كما تلاحظون إخواني في الصورة على اليمين لدينا تعليمات بلغة الأسمبلي ويوجد على اليسار مايوافق

كل تعليمة من opcodes .

هذه الأكواد مكتوبة بالنظام السداسي العشري ، فنقوم الآن بكتابتهآ في السكريبت نسميه مثلا shellcode.c وذلك بوضعها في صف واحد نسميه مثلا shellcode مع إضافة \x وطبعا نظيف الوظيفة ()main لجعل السكريبت قابل للتنفيذكما في الصورة التالية :



نقوم الآن بعملية compilation ونجرب الشيل كود تبعنا كما في الصورة التالية :



في الحقيقة هذا الشيل كود الذي كتبناه غير مستعمل في العالم الحقيقي لكتابة الثغرات .

لمذا ؟

نرجع للشيل كود تبعنا :
كود:
\xbb\x00\x00\x00\x00\xb8\x01\x00\x00\x00\xcd\x80

طيب إخواني لو نلاحظ أن الشيل كود تبعنا يحتوي على رموز مصفرة أي على \x00 ونحن قلنا أننا سنقوم بحقن هذا الشيلكود في البافر المخصصة لمدخلات المستخدم وهذه الرموز المصفرة ستفشل عملية الحقن وذلك لأن هذا الرمز \x00يستعمل لإنهاء السلاسل .

فيجب أن نجد طريقة لإستبدال هذه الرموز المصفرة برموز غير مصفرة مع الحفاظ على مهمة الشيل كود في نفس الوقت.

عموما توجد طريقتين لفعل ذلك :

الأولى : نقوم بكل بساطة بإستبدال التعليمات المكتوبة بلغة الأسمبلي والمتسببة في إنتاج هذه الرموز السيئة بتعليمات أخرى لاتنتج رموز سيئة وطبعا مع الحفاظ على نفس المهمة .

الثانية :

أصعب قليلا من الأولى ويتوجب علينا تحديد مكان الشيل كود في الذاكرة للقيام بها .


سنستعمل في هذا الدرس الطريقة الأولى لإزالة الرموز السيئة وحنشوف الطريقة الثانية لاحقا بإذن الله .

نرجع الآن إلى الصورة التي فيها تعليمات الأسمبلي ومايقابلها من opcodes كالآتي :



نلاحظ أن التعليمة الأولى والتعليمة الثانية هما المتسببتان في إنتاج الرموز السيئة أو المصفرة .

نبدأ بالتعليمة الأولى :

mov $0x0, %ebx
0x0هو بنظام الهيكس يعني يساوي 0 في النظام العشري .

ماهي مهمة هذه التعليمة ؟

تقوم هذه التعليمة بتحميل المسجل EBX بالرقم 0

طيب كيف يمكننا أن نجعل من الناتج يساوي 0 بدون إستعمال الرقم 0 عند كتابة التعليمة ؟

لدينا التعليمة المنطقية XOR في لغة الأسمبلي ترجع لنا القيمة 0إذا قمنا بمقارنة مسجلين نعلم أنهما متساويان .

يعني يمكننا أن نتحصل على القيمة0 من دون إستعمال القيمة 0داخل التعليمة .
وبما أننا لانريد أن نتحصل على رموز مصفرة فعوض أن نستعمل التعليمة mov لتحميل المسجل EBX بالقيمة 0يمكننا في ذلك إستعمال التعليمة XOR

يعني تقوم بمقارنة ebx بال ebx وبما أن ebx = ebx فالناتج حيكون 0

فأول تعليمة تبعنا :

mov $0x0, %ebx
تصبح :

xor %ebx, %ebx

ننتقل إلى التعليمة الثانية :


mov $0x1 , %eax

نلاحظ الآن في هذه التعليمة لم نستعمل القيمة 0 وورغم ذلك أنتجت التعليمة رموز مصفرة .

فمن أين أتت الرموز المصفرة إذا ؟

نحن إستعملنا في هذه التعليمة المسجل EAXوهو من عرض 32 بت .

وقمنا بتحميله فقط بالرقم 1 يعني حملناه فقط ببينات حجمها 1 بت ولكن المسجل ممتد على 32 بت فتم ملئ 8 بت الأولى منه بالرقم 1 والباقي تم ملئه بأصفار لتعويض الأماكن الفارغة مثلما لدينا مثلا
1 = 00001

فالأصفار الزائدة لاتؤثر في القيمة .

يمكننا تخطي هذا المشكل وذلك بإستعمال المسجل ALكما هو موضح في الصورة :



المسجل EAX من عرض 32 بت مقسم إلى مساحتين

بحجم 16 بت فيمكن الوصول إلى 16 بت الأولى تبعه من خلال المسجل AX وأيضا يمكن الوصول إلى 8 بت الأولى تبعه من خلال المسجل AL
وهذه المسجلات مربوطة ببعض يعني إذا قمنا بتغيير قيمة إحداهما فيتغير معها الباقي .

يعني ببساطة نحول الرقم 1 والذي قلنا حجمه 8 بت إلى المسجل AL مع الحفاظ على قيمة EAX
وبما أننا ملئنا كل المسجل Al فلن تبقى فيه أماكن فارغة وبذلك نتخلص من الرموز المصفرة يعني التعليمة تصبح كالآتي :

movb $1, %al
يعني الآن أصبح الكود تبعنا كالآتي :

xor %ebx, %ebx
movb $1, %al
int $0x80
طيب إخواني الآن بعدما قملنا بإزالة الرموز المصفرة تعالو نتأكد من ذلك بوضع هذه التعليمات في برنامج بالأسمبلي كالآتي :



نقوم بعملية compilation ثم نحوله إلى opcodes كالآتي :



كما تلاحظون إخواني تم إزالة الرموز المصفرة وإضافة إلى ذلك تم تقليص حجم الشيل كود تبعنا .

نكمل الخطوات كالسابق نضع الشيل كود في كود السي كما هو موضح في الصورة التالية :




ثم نقوم بعملية compilation وننفذه كالآتي :



طبعا نحن إلى حد الآن قمنا بكتابة شيل كود بسيط لل exit system call بقي لنا أن نقوم بحقنه وتنفيذه في الذاكرة وذلك بإدخاله في main()

المشاركات الشائعة من هذه المدونة

بحث مرتقب بخصوص علم البيانات الضخمة Big Data وتحليلها - تدوينة للإعداد

توضيحات بخصوص اساليب التخفي - The Onion Router

أسئلة شائعة حول إضافة Mailvelope والإجابة عليها