Blog

Back to Blog
Buferni to'ldirish hujumlari nima va ular qanday qilib oldini oladi?

Buferni to’ldirish hujumlari nima va ular qanday qilib oldini oladi?

1988 yildagi Morris qurti sanoatni larzaga keltiruvchi tajribalardan biri bo‘lib, qurt buferning to‘lib ketishi yoki buferning to‘lib ketishi deb nomlanuvchi zaiflik yordamida qanchalik tez tarqalishini ko‘rsatdi. Internetning kashshofi bo’lgan ARPANET tarmog’iga ulangan 60 000 ta kompyuterdan 6 000 ga yaqini Morris qurti bilan zararlangan. Hujum ba’zi ijobiy natijalarga erishgan bo’lsa-da, xususan, dasturiy ta’minot ishlab chiqaruvchilarini zaifliklarni jiddiy qabul qilishga undash va birinchi kompyuter favqulodda vaziyatlarga javob berish guruhini (CERT) yaratishda, bufer to’lib ketishidan foydalanish oxirgi hujumdan uzoq edi.

2001 yilda Code Red qurti Microsoft IIS dasturi bilan ishlaydigan 359 000 dan ortiq kompyuterlarni yuqtirgan. Code Red veb-sahifalarni buzdi va xizmat ko’rsatishni rad etish hujumlarini, shu jumladan Oq uy veb-serverida ham amalga oshirishga harakat qildi.

Keyin, 2003 yilda SQL Slammer qurti Microsoft SQL Server dasturiy ta’minotida ishlaydigan 250 000 dan ortiq tizimlarga hujum qildi. SQL Slammer marshrutizatorlarni buzdi, bu Internetdagi tarmoq trafigini sezilarli darajada sekinlashtirdi yoki hatto to’xtatdi. Code Red va SQL Slammer qurtlari buferni to’ldirish zaifliklari orqali tarqaladi.

Morris qurtidan o’ttiz yildan ko’proq vaqt o’tgan bo’lsa ham, biz hali ham buferning to’lib ketishining zaifliklari va ularning barcha salbiy oqibatlari bilan azoblanamiz. Ba’zilar turli dasturlash tillarini yoki ularning xususiyatlarini ishonchsiz dizaynda ayblashsa-da, ayb ko’proq ushbu tillardan noto’g’ri foydalanishdadir. Bufer to’lib ketishi qanday sodir bo’lishini tushunish uchun biz xotira, ayniqsa stek haqida va dasturiy ta’minot ishlab chiquvchilari kod yozishda xotirani qanday ehtiyotkorlik bilan boshqarishlari kerakligi haqida ozgina bilishimiz kerak.

Bufer nima va u qanday toshib ketadi?

Bufer – bu operatsion tizim tomonidan dasturiy ta’minot dasturiga tayinlangan xotira blokidir. Dastur operatsion tizimdan to’g’ri ishlashi uchun kerakli xotira miqdorini so’rashi kerak. Java, C#, Python, Go va Rust kabi ba’zi dasturlash tillarida xotirani boshqarish avtomatik tarzda amalga oshiriladi. C va C++ kabi boshqa tillarda dasturchilar xotirani ajratish va ajratishni qo‘lda boshqarishi va bufer uzunligini tekshirish orqali xotira chegaralarini kesib o‘tmasligini ta’minlashi kerak.

Biroq, kod kutubxonalarini noto’g’ri ishlatadigan dasturchilar yoki ularni yozuvchilar tomonidan xatolar bo’lishi mumkin. Bu kashfiyot va ekspluatatsiya uchun tayyor bo’lgan ko’plab dasturiy zaifliklarning sababidir. To’g’ri ishlab chiqilgan dastur ma’lumotlarni saqlash uchun maksimal xotira hajmini belgilashi va bu hajmdan oshmasligini ta’minlashi kerak. Bufer to’lib ketishi dastur o’ziga tayinlangan xotiradan tashqari ma’lumotlarni boshqa foydalanish uchun mo’ljallangan yoki boshqa jarayonga tegishli bo’lgan qo’shni xotira blokiga yozganda sodir bo’ladi.

Bufer to’lib ketishining ikkita asosiy turi mavjud bo’lgani uchun – to’pga asoslangan va stekga asoslangan, to’p va stek o’rtasidagi farq haqida dastlabki so’z tartibda.

Stack va Heap

Dastur bajarilishidan oldin, yuklovchi unga virtual manzil maydonini tayinlaydi, u ham yig’ish, ham stek manzillarini o’z ichiga oladi. Uyum – bu global o’zgaruvchilar va ish vaqtida xotira tayinlangan (dinamik ravishda ajratilgan) o’zgaruvchilar uchun ishlatiladigan xotira blokidir.

Bufetdagi plastinalar to’plami kabi, dastur stegi chaqirilayotgan funktsiyaning mahalliy o’zgaruvchilarini o’z ichiga olgan ramkalardan iborat. Funktsiyalar chaqirilganda freymlar stekga suriladi va funksiyalar qaytganida o’chiriladi (o’chiriladi). Agar bir nechta iplar mavjud bo’lsa, bir nechta steklar mavjud.

Yig’ma bilan solishtirganda stack juda tez, lekin uni ishlatishning ikkita kamchiliklari bor. Birinchidan, stek xotirasi cheklangan, ya’ni katta ma’lumotlar tuzilmalarini stekga joylashtirish mavjud manzillarni tezda tugatadi. Ikkinchidan, har bir freymning stekdagi mavjudligi bilan chegaralangan umri bor, ya’ni stekdan chiqarilgan kadrdan ma’lumotlarga kirish yaroqsiz. Agar bir nechta funktsiyalar bir xil ma’lumotlarga kirishi kerak bo’lsa, ma’lumotlarni to’pga joylashtirish va ushbu ma’lumotlarga (uning manziliga) ko’rsatgichni ushbu funktsiyalarga o’tkazish yaxshiroqdir.

Bufer to’lib ketishi ham to’pda, ham stekda sodir bo’lishi mumkin, ammo bu erda biz keng tarqalgan xilma-xillikka e’tibor qaratamiz: stekga asoslangan bufer to’lib ketishi.

Stack bufer to’lib ketishi: Qaytish manzili ustidan yozilgan

Funktsiya har safar chaqirilganda freymlar bir-birining ustiga yig’ilganligi sababli, qaytariladigan manzillar ham stekga suriladi va dasturga chaqirilgan funksiya tugagandan so’ng ijroni qaerda davom ettirish kerakligini aytadi:

Buferni to'ldirish hujumlari nima va ular qanday qilib oldini oladi?

Qaytish manzili mahalliy o’zgaruvchilarni saqlaydigan buferlar yonida joylashgan. Shuning uchun, agar zararli dastur buferga saqlashi mumkin bo’lganidan ko’proq ma’lumot yozishga muvaffaq bo’lsa, bufer to’lib ketishi sodir bo’ladi. Belgilangan buferga to’g’ri kelmaydigan ma’lumotlar qaytish manziliga to’kilishi va uning ustiga yozishi mumkin.

Agar zaif dasturdan odatiy foydalanish paytida bufer to’lib ketishi sodir bo’lsa, ko’pincha qayta yozilgan qaytarish manzilining yangi qiymati xotiradagi haqiqiy joy emas, ya’ni dastur xotira segmentatsiyasi xatosini keltirib chiqaradi va xatoni tuzatishni talab qiladi – agar buning iloji bo’lmasa, dastur stek freymlari orqali o’zgartirilgan funksiyadan qaytishga harakat qilganda beqaror bo’lib qolishi yoki hatto ishdan chiqishi mumkin. Biroq, kiberjinoyatchilar bufer to’lib ketishidan foydalanib, qaytish manzilini xotirada to’g’ridan-to’g’ri zararli kodiga ishora qiluvchi to’g’ri joy bilan qayta yozishi mumkin, bu ularga ko’p hollarda qobiqlarni ishga tushirish va jabrlanuvchi kompyuterlar ustidan to’liq nazoratni qo’lga kiritish imkonini beradi. Masalan, Stuxnet qurti ildiz qobig’ini ishga tushirish uchun buferning to’lib ketishi zaifligidan foydalangan.

Ba’zi ekspluatatsiya kodlari asl qaytarish manzilini tiklash uchun zararli harakat bajarilgandan so’ng stekdagi buzilishlarni tuzatish uchun hatto aqlli yondashuvdan foydalanadi. Shunday qilib, tajovuzkorlar dasturning keyinchalik to’g’ri ishlashiga imkon berib, qaytarish yo’riqnomasining o’g’irlanishini yashirishga harakat qilishadi.

Misol – bayt qiymatlari sifatida o’n oltilik belgilarni kodlash

2021-yilda aniqlangan so‘nggi bufer to‘lib ketishiga qiziqqan dasturiy ta’minot ishlab chiqaruvchilari uchun biz quyidagi C kodini taqdim etamiz, bu CVE-2021-21748 sifatida kuzatilgan ZTE MF971R LTE routeridagi zaiflikning soddalashtirilgan va qayta yozilgan versiyasidir:

#include
#include
void encodeHexAsByteValues(const char *hexString, char *byteValues) {
signed int offset = 0;
int hexValues = 0;
int byte = 0;
while (offset < strlen(hexString)) {
strncpy((char *) &hexValues, &hexString[2 * offset], 2u);
sscanf((const char *) &hexValues, «%02X», &byte);
byteValues[offset++] = byte; // The return address can be overwritten opening a path for the
// insertion of exploit code
}
}
int main(void) {
const char* hexString = «0123456789ABCDEF01234»;
char byteValues[4];
encodeHexAsByteValues(hexString, byteValues); // There is no size check to ensure that
// hexString is not too long for byteValues
// before calling the function
return 0;
}

Yuqoridagi dastur o’n oltilik raqamli mos belgilar qatorini xotira talablarining yarmiga teng shaklga kodlaydigan funksiyani namoyish etadi. Ikkita belgi haqiqiy bayt qiymatlari sifatida (o’n oltilik tizimda) turishi mumkin, shuning uchun mos ravishda 30 va 31 bayt qiymatlari bilan ifodalangan «0» va «1» belgilar tom ma’noda 01 bayt qiymati sifatida ko’rsatilishi mumkin. Bu funksiya ZTE routerida parol bilan ishlashning bir qismi sifatida ishlatilgan.

Kod izohlarida ta’kidlanganidek, hajmi 21 belgidan iborat hexString baytValues ​​buferi uchun juda katta, uning hajmi atigi 4 ta belgidan iborat (garchi u kodlangan shaklda 8 tagacha belgini qabul qilishi mumkin bo’lsa ham) va encodeHexAsByteValues ​​buff funksiyasini oshirib yubormasligiga ishonch hosil qilish uchun hech qanday tekshiruv yo’q.

Buferni to’ldirish hujumlaridan himoya qilish

Dasturiy ta’minotni ishlab chiquvchilar tomonidan puxta dasturlash va sinovdan o’tkazishdan tashqari, zamonaviy kompilyatorlar va operatsion tizimlar bufer to’lib ketish hujumlarini amalga oshirishni qiyinlashtiradigan bir nechta mexanizmlarni amalga oshirdilar. Linux uchun GCC kompilyator drayverini misol qilib olib, biz buferning to’lib ketishining oldini olish uchun foydalanadigan ikkita mexanizmni qisqacha aytib o’tamiz: stekni tasodifiylashtirish va stek korruptsiyasini aniqlash.

Stack randomizatsiyasi

Buferdan oshib ketish hujumlari muvaffaqiyatining bir qismi ekspluatatsiya kodiga ishora qiluvchi xotiradagi haqiqiy joyni bilishga bog’liq. Ilgari, stekning joylashuvi bir xil edi, chunki dasturlar va operatsion tizim versiyalarining bir xil kombinatsiyasi bir xil stek manzillariga ega edi. Bu shuni anglatadiki, tajovuzkorlar bir xil dastur va operatsion tizim kombinatsiyasiga hujum qilish uchun bitta biologik virus shtammi kabi bitta hujumni tashkil qilishlari mumkin edi.

Stack randomizatsiyasi dasturning bajarilishi boshida stek maydonining tasodifiy miqdorini ajratadi. Bu bo’shliq dastur tomonidan foydalanish uchun mo’ljallanmagan, lekin dastur har bir bajarilganda boshqa stek manziliga ega bo’lishiga imkon beradi.

Biroq, doimiy tajovuzkor turli manzillarni qayta-qayta sinab ko’rish orqali stek randomizatsiyasini engib o’tishi mumkin. Buning bir usuli – ekspluatatsiya kodining boshida dastur hisoblagichini oddiygina oshiradigan uzoq NOP (no-op) ko’rsatmalaridan foydalanish. Keyin tajovuzkor ekspluatatsiya kodining boshlanishining aniq manzilini taxmin qilishdan ko’ra, ko’p NOP ko’rsatmalaridan birining manzilini taxmin qilishi kerak. Bu «NOP sled» deb ataladi, chunki dastur ushbu NOP ko’rsatmalaridan biriga o’tgandan so’ng, ekspluatatsiya kodini ishga tushirishdan oldin qolgan NOPlar orqali ishlaydi. Masalan, Morris qurti 400 ta NOP ko’rsatmalari bilan boshlangan.

Dasturning boshqa qismlari, masalan, dastur kodi, kutubxona kodi, global o’zgaruvchilar va yig’ma ma’lumotlar, dastur har safar ishga tushirilganda turli xil xotira manzillariga ega bo’lishini ta’minlash uchun manzillar maydoni tartibini tasodifiylashtirish deb ataladigan butun texnikalar sinfi mavjud.

Stack korruptsiyasini aniqlash

Bufer to’lib ketishining oldini olishning yana bir usuli – bu stekning buzilishini aniqlash. Umumiy mexanizm stek qo’riqchisi sifatida tanilgan bo’lib, u mahalliy stek ramkasi buferlari va stekning qolgan qismi o’rtasida tasodifiy kanariya qiymatini, shuningdek himoya qiymati deb ham ataladi. Funktsiyadan qaytishdan oldin, dastur kanareyka qiymatining holatini tekshirishi va agar buferning to’lib ketishi kanareyka qiymatini o’zgartirgan bo’lsa, xatoni qayta ishlash tartibini chaqirishi mumkin.

Buferni to'ldirish hujumlari nima va ular qanday qilib oldini oladi?

Yakuniy maslahat

Buferning to’lib ketishidagi zaifliklarni aniqlash va tuzatish davom etar ekan, eng yaxshi maslahat barcha ilovalar va kod kutubxonalarini eng yuqori ustuvorlikka ega bo’lgan tuzatish siyosatiga ega bo’lishdir. Yamoq siyosatingizni ekspluatatsiya kodini aniqlay oladigan xavfsizlik yechimlarini o‘rnatish bilan birlashtirish bufer to‘lib ketishidan foydalanishga urinayotgan tajovuzkorlarga nisbatan xavfni sezilarli darajada oshirishi mumkin.

Share this post

Fikr bildirish

Back to Blog