السلام عليكم ورحمه الله ,,,
-
معظم البرامج والمواقع ومختلف التطبيقات التي تكتب , تمر بهذة المرحلة أو هذا الإختصاص ( أمن التطبيقات )
بإختصار : مجال أمن التطبقات هو البحث في الشفرة المصدرية لأي تطبيق عن إمكانية حدوث ثغرة أمنية!
أو البحث عن إحتمال إستغلال شفرة معينة مكتوبة في التطبيق لتنفيذ أي أمر وبأي طريقة,,,
سنقوم بكتابة هذا الموضوع في قسمين :
القسم الأول : مجال أمن التطبيقات للملفات التنفيذية, وهو يبحث طرق إيجاد الثغرات وتجنبها
في الشفرة المصدرية للملفات التنفيذية , تكتب الملفات التنفيذية بعدة لغات مثل
لغة الإسمبلي , السي , البيسك , الباسكال ... وغيرها
أنواع الثغرات في هذا المجال Buffer Overflow بأنواعها stack,heap.. وثغرات Format String و File Handling ..وغيرها
القسم الثاني : مجال أمن تطبيقات الإنترنت ومواقع الويب
وهو يبحث في إستغلال أخطاء لغات وسكربتات برمجة الوب perl ,asp , php ,jsp ..
أنواع الثغرات في هذا المجال Injection بأنواعها cmd ,sql... وثغرات CSS/XSS أو مايعرف Cross-Site Scripting وغيرها
نبدأ الموضوع
أي برنامج أو تطبيق مكتوب بلغة برمجة يمر بعدة مراحل , وهي
الفكرة وبعد ذلك مرحلة كتابة التطبيق بلغة برمجة وبعد ذلك تنقيح الأخطاء
وبعد هذة المراحل يكون التطبيق جاهز للتنفيذ وبدون أخطاء برمجية .
وهنا تبدأ مرحلة جديدة وهي أمن التطبيق , وهي البحث عن أي تعليمة يمكن إستغلالها في الكود الذي قمنا بكتابتة
لتنفيذ أو تغيير مجرى التنفيذ , ماهي الخطوات والحلول والأساليب المتبعة في هذا المجال؟!!
أولاً : الملفات التنفيذية
الملفات التنفيذية تكتب بعدة لغات , ولكن الحلول والخطوات متشابهة في كل لغات البرمجة
لأن كل اللغات تعتمد على دوال API , ولذلك سنستخدم لغة السي في بقية الأمثلة
-
أغلب الثغرات في كتابة الملفات التنفيذية هي ثغرات الفيض في مخزن البيانات ,, ومكانها الذاكرة
ولذلك تجد أهم الدوال المستخدمة في أمن التطبيقات محصورة في دوال الذاكرة مثل
FillMemory CopyMemory MoveMemory - IsBadReadPtr IsBadWritePtr IsBadStringPtr - إستخدام دوال إعادة التحجيم LocalReAlloc LocalSize GlobalReAlloc HeapReAlloc ....وغيرها
-
ولآن سنحاول كتابة المثال المشهور لإحداث ثغرة فيض .. وبعد ذلك سنكتب الحلول
الكود:
#include<stdio.h>
#include<string.h>
#include<windows.h>
#include<winbase.h>
int main(int argc,char *argv[]){
char name[200];
if (argc != 2)
return 1;
lstrcpy(name,argv[1]);
printf(name);
return 0;
}هذا المثال عبارة عن برنامج بسيط , يستقبل مدخلات عبارة عن إسم المستخدم ويقوم بطباعتها على الشاشة
بالتأكيد الكود السابق صحيح وبدون أخطاء,, ,وكما هو واضح تم تحديد طول إسم المستخدم 200 حرف ,
ومن المستحيل وجود إسم طولة أكثر من 200 حرف ,, ومع ذلك يعتبر هذا الكود خطأ أمني يمكن أن يستغل
لحدوث فيض وإدخال أي برنامج أو shellcode والطريقة معروفة .. إدخال حجم بيانات أكبر من 200 حرف
والحل في مثل هذة الظروف يكون بإستخدام تعليمات تقييد المدخلات وهي تعليمات مهمة ,, لاحظ تغيير التعليمة
lstrcpy(name,argv[1]);
إلى
CopyMemory(name,argv[1],199);
لاحظ كيف قيدنا أي مدخلات لمخزن البيانات بحجم لا يتجاوز 200 بايت من 0 إلى 199 =200 بايت
هذة القاعدة مهمة جداً في أمن التطبيقات
القاعدة الأولى : تقييد المدخلات لمخزن البيانات
إستخدام الدالة CopyMemory بدل أغلب دوال النصوص التي تتعامل مع مخزن بيانات متغير مثل lstrcpy,lstrcat ....وغيرها
إستخدام المايكرو memcpy بدل المايكروstrcpy,strcat .......... وهكذا
-
مثال بسيط لأهمية هذة القاعدة ,,,
قبل فترة قمت بدراسة لبرنامج مشهور وهو مشغل الفلاش Macromedia Flash Player 6 في حزمة MX
وفي مراحل مراقبة طريقة تقسيم ملف الفلاش وطريقة قرائتة ,, أعتقدت أني وجدت خطأ وهو طريقة قرائة
إسم الخط المستخدم في ملف الفلاش ؟ يتم نقل إسم الخط مباشرة إلى المكدس!! وهنا وجدت خطأ مؤكد لإكتشاف ثغرة
وبعد كل الخطوات الناجحة وإيصال التنفيذ إلى مرحلة نقل التحكم وتنفيذ الثغرة وجدت الحاجز الكبير الذي أحبط المهمة
وهو إستخدام تقييد لمخزن المدخلات memcpy ,, لاحظ الصورة
الخطوة الثانية : التحقق من إمكانية الوصول إلى ذاكرة مخزن البيانات
دوال التحقق وهي : IsBadStringPtr , IsBadWritePtr ,IsBadCodePtr...
هذة الدوال مهمة جداً في عمل البرامج ومع ذلك لم أرى برنامج عربي يستخدمها
-
هل تريد نصيحة وطريقة تضمن لك ثبات أي برنامج تقوم بكتابتة ؟ فقط إحفظ هذة القواعد
1 - إذا قمت بتعريف أي متغير في برنامجك ,, وقبل أن تخزن بة أي بيانات دائما تحقق من إمكانية الكتابة
مثلاً في برنامجنا السابق نضيف ,, قبل الكتابة للمتغير name
if(IsBadWritePtr (name,200)==0){
هنا نضيف دوال الكتابة للمتغير
}هذا البلوك أضفة في أي مكان في البرنامج تجد عملية كتابة للمتغير name
2- تحقق من العنوان الذاكري قبل أي عملية قرائة للمتغير
مثال
if(IsBadReadPtr (name,200)==0){
memcpy(des,name);3 - قبل أن تستخدم أي سلسلة نصية ,, فقط مررها على الدالة IsBadStringPtr
4- قبل أن تتصل بأي دالة أو عنوان تنفيذي ... تحقق من وجودة بهذة الدالة IsBadCodePtr
وتوجد طرق أخرى كثيرة ...
--
لضيق الوقت سنكمل هذة السلسلة من الدروس بالأقساط
وبالتوفيق ,,,