در این مقاله انواع حملات Buffer Overflow (یا سرریز بافر) را بررسی کرده و بهترین شیوههای کدگذاری ایمن که از بروز این آسیبپذیری جلوگیری میکنند و همچنین مراحل پس از استقرار برای ایمن نگه داشتن برنامهها و وبسایتها را بیان خواهیم کرد.
حملات سرریز بافر میتوانند با ایجاد بستری مناسب برای مهاجم به منظور حمله به برنامه و به دست آوردن کنترل شبکه، آسیب جدی به سازمان وارد کنند. اولین حمله سرریز بافر ثبت شده در سال 1988، کرم Morris بود. علیرغم عمر طولانی و تاکتیکهای شناخته شده این حملات، دفاع در برابر آنها دشوار است.
برای کاهش موفقیت حملات سرریز بافر، مهم است بدانیم آنها چگونه کار میکنند. برای آشنایی با نحوه کار حملات Buffer Overflow این مطلب را تا انتها بخوانید.
Buffer Overflow چیست و چگونه کار میکند؟
بافر یک قسمت موقت برای ذخیرهسازی دادهها است. هنگامی که مقدار دادههای تخصیص داده شده از ظرفیت بافر بیشتر شود، دادههای اضافی سرریز میشوند از اینرو به آن سرریز بافر میگویند. این مساله ممکن است باعث نشت دادهها به بافرهای دیگر شود و میتواند محتوای آنها را خراب یا overwrite کند.
در یک حمله سرریز بافر، یک عامل مخرب از نرم افزار آسیب پذیر سوء استفاده میکند. دو نوع اصلی سرریز بافر وجود دارد:
1. حملات سرریز بافر مبتنی بر Heap
پشته یک ساختار حافظه است که برای مدیریت حافظه پویا استفاده میشود. برنامه نویسان معمولا از پشته برای تخصیص حافظهای استفاده میکنند که اندازه آن در زمان کامپایل مشخص نیست و مقدار حافظه مورد نیاز آنقدر زیاد است که در پشته جا نمیشود یا حافظه برای استفاده در فراخوانی تابع در نظر گرفته شده است.
حملات مبتنی بر Heap فضای حافظهای که برای یک برنامه یا فرآیند در نظر گرفته شده را پر میکنند. آسیبپذیریهای مبتنی بر Heap مانند باگ zero-day، به سختی قابل سوء استفاده هستند، بنابراین از حملات پشتهای نادرتر هستند.
2. حمله سرریز بافر مبتنی بر پشته
پشته یک فضای پیوسته در حافظه است که برای سازماندهی دادههای مرتبط با فراخوانی تابع، از جمله پارامترهای تابع، متغیرهای محلی تابع و اطلاعات مدیریتی، مانند فریم و نشانگرهای دستورالعمل، استفاده میشود. پشته دادهها را به صورت آخرین ورودی – اولین خروجی نگهداری میکند.
به طور معمول، پشته خالی است تا زمانی که برنامه مورد نظر به ورودی کاربر، مانند نام کاربری یا رمز عبور نیاز داشته باشد. در آن مرحله، برنامه یک آدرس حافظه بازگشتی در پشته مینویسد و سپس ورودی کاربر در بالای آن قرار میگیرد. هنگامی که پشته پردازش میشود، ورودی کاربر به آدرس بازگشتی مشخص شده توسط برنامه ارسال میشود.
با این حال، پشته اندازه محدودی دارد. برنامهنویس توسعه دهنده کد، باید فضایی را برای پشته در نظر بگیرد. اگر ورودی کاربر بیش از فضای پشته باشد و برنامه تأیید نکند که ورودی مناسب است، پشته سرریز خواهد کرد. این مساله بهخودیخود مشکل بزرگی نیست، اما زمانی که با ورودیهای مخرب ترکیب شود، به یک حفره امنیتی بزرگ تبدیل خواهد شد.
اگر یک عامل مخرب دادههایی بیش از ظرفیت پشته بافر را به آن ارسال کند، ممکن است دادههای مجاز توسط بدافزاری که میتواند به فایلها آسیب برساند، دادهها را تغییر دهد یا اطلاعات خصوصی را فاش کند، جابجا شود.
استفاده از یک رشته ورودی طولانیتر از مقدار فضای رزرو شده، باعث ایجاد حفره امنیتی میشود. هکرها ابتدا از ابزارهای اسکن برای یافتن برنامههای آسیبپذیر در برابر سرریز استفاده کرده و سپس حمله را آغاز میکنند.
هنگامی که کد مخرب باعث سرریز شد، هکر با نشان دادن آدرس بازگشتی که به دستور اشاره میکند، آن را اجرا میکند. بافر باعث میشود برنامه تقریبا از کار بیفتد، اما سعی میکند با رفتن به آدرس بازگشتی که توسط هکر به دستور مخرب هدایت شده، آن را بازیابی کند.
هنگامی که حمله Buffer Overflow دستوری که در آدرس بازگشتی جدید قرار دارد را اجرا میکند، برنامه فکر میکند هنوز در حال اجرا است. این بدان معناست که پنجره خط فرمانی که باز شده است با همان مجموعه مجوزهای اجرایی برنامۀ در معرض خطر اجرا میشود و هکر را قادر میسازد تا کنترل کامل سیستم عامل را به دست آورد.
3. حمله سرریز integer
اکثر زبانهای برنامهنویسی حداکثر اندازهای را برای اعداد صحیح تعریف میکنند. هنگامی که از این اندازه تجاوز شود، نتیجه ممکن است باعث خطا شده یا نتیجه نادرستی را در محدوده اعداد صحیح نشان دهد.
حمله سرریز Integer زمانی رخ میدهد که یک عدد صحیح در عملیات محاسباتی استفاده شود و نتیجه مقداری بیش از حداکثر طول عدد صحیح باشد. به عنوان مثال برای ذخیره عدد 192 به 8 بیت حافظه نیاز است. اگر فرآیند 64 را به این عدد اضافه کند، پاسخ 256 در حافظه اختصاص داده شده جای نمیگیرد، زیرا به 9 بیت نیاز دارد.
4. حمله سرریز Unicode
این حملات از حافظه مورد نیاز برای ذخیره یک رشته در فرمت Unicode که نسبت به کاراکترهای کد استاندارد آمریکایی برای تبادل اطلاعات (ASCII) بیشتر است، سوء استفاده میکنند. این فرمت را میتوان در برنامههایی استفاده کرد که تمام ورودیهای آن باید از نوع کاراکترهای ASCII باشد.
5. حملات format string
مهاجمان با استفاده نادرست از توابع کتابخانه فرمتبندی رشته مانند printf و sprintf، برای دسترسی و دستکاری سایر فضاهای حافظه، نحوه جریان برنامه را تغییر میدهند.
چگونه از حملات سرریز بافر جلوگیری کنیم
پس از درک نحوه عملکرد یک حمله Buffer Overflow، سازمانها میتوانند نحوه جلوگیری از نفوذ آنها به سیستم و کنترل برنامههای خود را بهتر درک کنند.
برای افزایش سطح دفاعی سازمان، از شیوههای کدگذاری ایمن استفاده کرده و با رعایت موارد زیر از آسیبپذیریهای سرریز بافر جلوگیری کنید:
1. از حفاظتهای زمان اجرای سیستم عامل استفاده کنید.
اکثر سیستمعاملها از حفاظت در زمان اجرا استفاده میکنند، تا احتمال موفقیت حملات بافر سرریز را کاهش دهند مانند:
- تصادفیسازی طرحبندی فضای آدرس یا ASLR، موقعیتهای فضای آدرس دادههای کلیدی یک فرآیند را بهطور تصادفی مرتب میکند که شامل سطح قابل اجرا و موقعیتهای پشته، heap و کتابخانهها میشود. این رویکرد پرش قابل اعتماد به یک عملکرد خاص در حافظه را برای مهاجم دشوار میکند.
- Data Execution Prevention (پیشگیری از اجرای دادهها) مناطقی از حافظه را به عنوان قابل اجرا یا غیرقابل اجرا علامت گذاری میکند. این امر مانع از آن میشود که مهاجم بتواند دستورالعملهای نوشته شده در یک منطقه داده را از طریق سرریز بافر اجرا کند.
- حفاظت از بازنویسی مدیریت استثناهای ساخت یافته، برای جلوگیری از حملاتی طراحی شده که از تکنیک بازنویسی Structured Exception Handler استفاده میکنند، که شامل استفاده از سرریز بافر مبتنی بر پشته است.
2. patch دستگاهها را دریافت کنید.
فروشندگان patchها و آپدیتهای نرمافزاری را برای رفع آسیبپذیریهای سرریز بافری که کشف کرده اند صادر میکنند. هر چند بین کشف آسیبپذیری و ایجاد و استقرار patch هنوز هم یک دوره ریسک وجود دارد.
3. از اصل حداقل امتیاز (POLP) پیروی کنید.
به کاربران و برنامههای کاربردی فقط باید مجوزهایی داده شود که برای انجام کار خود یا انجام وظایف محول شده نیاز دارند. پیروی از رویکرد POLP احتمال وقوع حمله سرریز بافر را کاهش میدهد.
در مثال حمله سرریز پشته که در بالا به آن اشاره شد، پنجره خط فرمان باز شده با همان مجوزهایی اجرا میشود که برنامه را در معرض خطر قرار میدهد. هر چه مجوزهای کمتری داشته باشد، مهاجم کمتری خواهد داشت. در صورت امکان، فقط به کاربران و برنامهها مجوز موقت بدهید و پس از تکمیل کار، آن را حذف کنید.
4. از زبانهای برنامهنویسی ایمن حافظه استفاده کنید.
رایجترین دلیل وقوع حملات سرریز بافر اینست که برنامهها در مدیریت تخصیص حافظه و اعتبارسنجی ورودی از مشتری یا سایر فرآیندها ناکام هستند.
برنامههای توسعهیافته در C یا ++C باید از توابع کتابخانه استاندارد خطرناکی که محدوده آنها مشخص نشده، مانند gets، scanf و strcpy اجتناب کنند. در عوض، از کتابخانهها یا کلاسهایی استفاده کنند که برای اجرای ایمن رشته و سایر عملیات حافظه طراحی شدهاند.
بسیاری از زبانهای برنامهنویسی مدرن مانند #C، جاوا، جاوا اسکریپت Perl، پایتون و Net. دارای حفاظتهای داخلی برای جلوگیری از خطاهای کدنویسی Buffer Overflow هستند. با این حال، این بدان معنا نیست که آنها 100٪ از سرریز بافر در امان هستند، به خصوص زمانی که با برنامهها، سرویسها و کتابخانههای سایر زبانهای برنامهنویسی تعامل دارند.
5. اعتبارسنجی دادهها
برنامههای کاربردی موبایل و وب که در داخل توسعه مییابند همیشه باید ورودیها و دادههای کاربر از منابع غیرقابل اعتماد را تأیید کنند تا اطمینان حاصل شود که در محدوده مورد انتظار قرار دارند و از مقادیر بیش از حد طولانی ورودی جلوگیری کنند.
هر سیاست امنیتی برنامه باید قبل از بکارگیری از نظر اعتبارسنجی پتانسیل ورودی و آسیبپذیریهای سرریز بافر مورد بررسی و ارزیابی قرار گیرد.
6. از فایلهای کتابخانهای مخاطره آمیز اجتناب کنید.
بسیاری از فایلهای کتابخانهای که در زبانهای برنامهنویسی استفاده میشوند، ذاتاً اهداف ناامن و جذابی برای هکرها هستند. هر نقطه ضعفی که در فایل کتابخانهای یافت شود، در همه برنامههایی که از آن فایل کتابخانه استفاده میکنند نیز وجود خواهد داشت. از هیچ تابع کتابخانهای که بررسی نشده است استفاده نکنید.
7. ورودی مخرب را فیلتر کنید.
کدهای ذاتا خطرناک HTML و هر کاراکتری که میتواند باعث مشکلات پایگاه داده شود را فیلتر کنید. به عنوان مثال، در کد ASP (Active Server Pages) ، علامتهای apostrophe، quotation و ampersand نمادهای رزرو شده هستند و باید فیلتر شوند. اگر در ورودی کاربر باشند، باعث از کار افتادن برنامه میشود.
8. آزمایش پیش استقرار برنامهها
حتماً همه برنامهها را قبل از استفاده آزمایش کنید و سعی کنید به همه جنبهها نفوذ کنید تا از امنیت کدنویسی اطمینان حاصل کنید. اگر برنامه خراب شود، مشخص است مشکلی وجود دارد که باید قبل از اینکه هکر بتواند از آن سوء استفاده کند، برطرف شود.
آزمایش برنامهها و وبسایتها برای جلوگیری از آسیبپذیریهای Buffer Overflow پس از استقرار، بسیار مهم است. شرکتها باید مراحل زیر را انجام دهند:
- patcheهای منتشر شده توسط ارائه دهنده و آپدیتهای نرم افزار را به موقع نصب کنند.
- وب سایتها و برنامهها را با ابزارهای اسکن آسیبپذیری دستی و خودکار آزمایش کنند.
- آدرسهای IP مرتبط با عوامل مخرب را مسدود کنند.
- از سیستمهای تشخیص نفوذ و پیشگیری استفاده کنند که سوء استفاده های سرریز بافر شناخته شده را مسدود میکند.
- از بازرسی بسته عمیق برای نظارت بر ترافیک شبکه استفاده کنند.