Umuman olganda, buffer – bu foydalanuvchi tomonidan keyinchalik ishlov berish uchun yuboriladigan tarkibni saqlashga mo‘ljallangan xotira maydonidir. Ba’zi bufferlar dinamik o‘lchamga ega bo‘ladi, boshqalari esa oldindan ajratilgan, doimiy o‘lchamga ega bo‘ladi.

Buffer overflow – bu dasturlarga 1980-yillarning oxiridan beri zarar yetkazib kelayotgan eng qadimiy xotirani buzilishiga olib keluvchi zaifliklardan biridir. Yillar davomida ko‘plab himoya choralar ishlab chiqilgan bo‘lsa-da, ular hali ham dolzarbligicha qolmoqda.

Umumiy nuqtai nazardan qaralganda, buffer overflow zaifligi foydalanuvchi tomonidan yuborilgan tarkib stack chegarasidan oshib ketib, unga tutash bo‘lgan xotira hududini ham egallab olganda yuzaga keladi. Quyidagi diagrammada bunga misol keltirilgan:

1-rasm: Stack asosida ishlovchi Buffer Overflow – Ekspluatatsiya bosqichlari

Ushbu diagrammada 8 baytdan ortiq bo‘lmagan parolni saqlash uchun mo‘ljallangan buffer ko‘rsatilgan. Agar foydalanuvchi “password43” kabi ma’lumot yuborsa, so‘nggi “4” va “3” raqamlari bu bufferni ikki baytga ortiqcha yuklaydi. Agar bu holat to‘g‘ri boshqarilmasa, bu kutilmagan xatti-harakatlarga olib kelishi mumkin – biz buni tez orada ko‘rib chiqamiz.

💡

Buffer overflow ekspluatlarini yozish bu modul doirasidan tashqarida bo‘lsa-da, biz ularning qanday ishlashini tushunamiz. Sababi, haqiqiy xavfsizlik tekshiruvlarida ushbu hujum vektorlariga duch kelganimizda ularni qanday tahrirlashni bilishimiz kerak.

Xotirani buzishga olib keladigan zaifliklar dasturning turli qismlarida – masalan, heap yoki stackda – yuzaga kelishi mumkin. Heap dinamik boshqariladi va odatda katta hajmdagi, global kirish imkoni mavjud bo‘lgan ma’lumotlarni saqlaydi. Stack esa lokal funksiyalar ma’lumotlarini saqlash uchun mo‘ljallangan va odatda o‘lchami doimiy bo‘ladi.

Stack odatda butun sonlar yoki buffer kabi lokal o‘zgaruvchilarni o‘z ichiga oladi. Quyidagi qisqa C kodda buffer overflow qanday yuzaga kelishi mumkinligi ko‘rsatilgan:

buffer[64]
...
strcpy(buffer, argv[1]);

1-listing – Buffer e’lon qilish va foydalanuvchi ma’lumotini unga o‘tkazish

Yuqoridagi misolda, 64 belgidan iborat buffer e’lon qilingan va foydalanuvchining buyruq qatori argumenti strcpy() funksiyasi orqali unga nusxa ko‘chirilmoqda. Bu funksiya birinchi parametr sifatida berilgan bufferga ikkinchi parametrdagi manbani nusxalaydi. strcpy() xavfli hisoblanadi, chunki u manba satri bufferga sig‘adimi-yo‘qmi tekshirmaydi – bu esa dasturning kutilmagan xatti-harakatlariga olib keladi.

Stack faqat 64 bayt (ya’ni buffer uchun) va funksiyaning boshqa elementlari (funksiya argumentlari, qaytish manzili) uchun joy ajratadi. Return address – bu funksiya tugaganidan so‘ng bajarilishi kerak bo‘lgan keyingi ko‘rsatma manzili.

Agar foydalanuvchining kiriti bu buffer o‘lchamidan katta bo‘lsa, u return addressni ustiga yozib yuborishi mumkin.

Return addressni o‘zgartirish juda jiddiy natijalarga olib keladi, chunki funksiya yakunida ret ko‘rsatmasi ishlaydi va u return addressdagi manzilni EIP/RIP (ya’ni instruction pointer) ga yuklaydi – bu ko‘rsatgich hozirda bajarilayotgan kodni kuzatadi.

Agar hujumchi return addressni nazorat qila olsa, u butun dastur bajarilishini o‘z xohishiga ko‘ra boshqarishi mumkin. Keling, stack asosidagi buffer overflow hujumi bosqichlarini ko‘rib chiqamiz:

2-rasm: Stack-Based Buffer Overflow – Exploit bosqichlari

Ushbu rasmda stackning uch xil holati tasvirlangan. Chapdagi ustunda buffer ishga tushirilgan va unga xotirada joy ajratilgan. Pastki qismda, qizil rangda, return address to‘g‘ri manzilni o‘z ichiga olgan. O‘rtadagi holatda, foydalanuvchi 32 belgili ma’lumot yuborgan – bu bufferning yarmini to‘ldiradi. O‘ngdagi holatda esa foydalanuvchi 80 ta “A” belgisi yuborgan – bu 64 baytlik bufferni to‘liq to‘ldirib, ustiga return addressni ham “A” belgilari bilan to‘ldiradi.

"A" belgisi 16likda 0x41 ga teng. Shunday qilib, return address qiymati \\x41\\x41\\x41\\x41 bo‘lib yoziladi.