Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Безпека.docx
Скачиваний:
163
Добавлен:
31.08.2019
Размер:
6.2 Mб
Скачать

5.4. Помилки переповнення буфера

Помилки переповнення буфера (Buffer Overflows) — найпоширеніші з помилок, іцо призводять до появи вад захисту в програмних системах. Скориставшись ци ми вадами захисту, зловмисники можуть виконати будь-яку команду і отримати можливість контролювати всю систему.

Спочатку розглянемо основну ідею переповнення буфера. У будь-якому програмному коді програмісти організовують буфери для тимчасового зберігання й оброблення даних. Розмір буфера має бути таким, щоб дані, з одного боку, пов­ністю у ньому вміщались, а з іншого, щоб буфер не був надто великим, оскільки тоді він марно займатиме пам'ять. Для копіювання даних у буфер переважно використовують бібліотечні функції. Якщо робота ведеться з рядками символів, то деякі функції не зважають на попереднє обмеження довжини і здійснюють копіювання до кінця рядка, тобто до символу, що є ознакою кінця рядка. До таких функцій належать, наприклад, strcat(), strcpy(), sprintf(), vsprintf(), gets(), scanf(). Коли довжина рядка перевищує розмір буфера, копіювання триває, і частину рядка, що не вмістилась у буфері, буде записано замість даних, розташованих за його межами.

Зазначене повною мірою стосується мови програмування С, в якій ознакою кінця рядка є байт із нульовим значенням. У мові С така сама ситуація виникає під час роботи з масивами, оскільки автоматичну перевірку виходу за межі маси­ву не передбачено. Інші мови програмування можуть повністю або частково запо­бігати виникненню таких помилок.

Конкретні наслідки переповнення буфера залежать від того, яке значення ма­ли втрачені або модифіковані дані та в якій області пам'яті було розміщено бу­фер: у статичній, динамічній пам'яті чи у стеку.

Розглянемо, які можливості надає порушникам переповнення буфера (заува­жимо, що порушник, який розробляє атаку через переповнення буфера, належить до категорії хакерів).

5.4.1. Переповнення буфера у стеку

Переповнення буфера у стеку, або «зривання стека», є найвідомішою технологією використання переповнення буфера, яка, незважаючи на складність її реалізації, становить цілком реальну загрозу. Саме помилку переповнення буфера у стеку, що

існувала в демоні fingerd, успішно використовував мережний хробак, відомий як вірус Морріса (докладніше про нього йтиметься в розділі 6). Якщо у програмі ви­являють помилку переповнення буфера у стеку, її оголошують як критичну.

Локальні змінні, оголошені у процедурі (функції), компілятор, як правило, розміщує у стеку. Тому розміщення у стеку буфера — явище типове. Розглянемо структуру стека. Як відомо, на багатьох апаратних платформах, зокрема на персо­нальних комп'ютерах, стек зростає «зверху вниз», тобто в бік молодших (мен­ших) адрес пам'яті. Після занесення даних у стек, його покажчик автоматично (апаратно) зміщується в бік молодших адрес, а в разі видалення даних зі стеку — в бік старших. Якщо у функції оголошено кілька локальних змінних, вони розмі­щуються (або для них резервується місце) у стеку послідовно і неперервно. Поря­док розміщення залежить від компілятора, тобто першою у стек може потрапити або перша, або остання змінна. Якщо, наприклад, у функції є такий фрагмент [60]:

test_function () {

char а;

char buff[5];

char b;

}

то стан стека після виклику функції test_function() може бути таким, як на рис. 5.4. Різним може бути порядок розміщення змінних, тобто а і b можуть помі­нятися місцями.

Рис. 5.4. Стан стека після виклику функції test_function()

З наведеної схеми стають очевидними кілька важливих особливостей. По перше, оскільки стек зростає в бік молодших адрес, а змінні заповнюють пам'ять у напрямку зростання адрес, то переповнення буфера призведе до того, що будуть

змінені ті дані, які були занесені у стек раніше. У нашому випадку це змінна b, службова інформація, а далі — адреса повернення з функції, тобто адреса інструкції, що розташована за командою виклику функції. У багатьох архітектурах комп'ютерів адреса повернення розміщується саме у стеку. У таких архітектурах шляхом переповнення буфера у стеку можна не лише модифікувати значення

окремих змінних, але й передати керування у довільне місце.

Тепер розглянемо, за яких умов порушники матимуть можливість використати переповнення буфера. Атака може бути здійснена за таких умов:

  • локальні змінні розміщені у стеку;

  • стек зростає «вниз»;

  • існують програми, в яких є вразливий код на кшталт наведеної вище функції test_function( ).

Атака матиме сенс, якщо:

  • функція приймає рядок символів від користувача (з клавіатури, зі стандартного вводу, через мережу, від іншої програми);

  • функція виконується з більшими привілеями чи повноваженнями, ніж маг

користувач, від якого вона приймає дані.

За цих умов користувач-порушник має можливість змінити у стеку значення деякої змінної. Це може мати сенс, оскільки цілком імовірно, що така змінна матиме критичне для безпеки системи значення (наприклад, логічна змінна, яка визначає результат перевірки прав доступу).

Порушник може передати керування іншій функції за таких додаткових умов:

  • адресу повернення також розміщено у стеку;

  • дані у стеку можуть бути інтерпретовані як команди.

Порушника, що хоче лише передати керування певній системній функції, адреса якої в пам'яті йому відома, вдовольнить виконання першої умови. Виконання другої умови надає порушнику унікальну можливість — вставити програмний

код, який потрібно виконати, безпосередньо в рядок, що порушник передає у буфер, і на нього ж передати керування через адресу повернення. Хоча апаратні за­соби, зокрема процесори Intel х86 (про них ітиметься в розділі 10), розрізняють сегменти коду і стека та підтримують заборону виконання інструкцій, розміщених у стеку, моделі пам'яті поширених ОС ці заборони скасовують за допомогою повного перекриття сегментів. Підсумовуючи всі викладені вище умови здій­снення атак, можна дійти висновку, що більшість поширених ОС, зокрема UNIX і Windows, надають порушникам чимало можливостей щодо «зривання стека».

Ще раз переглянемо принципову схему атаки. Перед початком атаки в систем і функціонує певна вразлива програма, що виконується з високими привілеями. Вона приймає дані (наприклад, текстовий рядок) від непривілейованих користувачів (або від будь-кого з мережі). Порушник передає програмі спеціально підготовлений текстовий рядок. У результаті модифікуються важливі дані, до яких порушник із його повноваженнями доступу не мав, або виконується певний програмний код порушника з повноваженнями вразливої програми. Певний інтерес для порушників становлять програми зі встановленим атрибутом SUІD і майже

всі демони в UNIX, сервіси (служби) Windows, а також багато прикладних про­грам, які так чи інакше здійснюють виклик привілейованого коду. Детальніше технології переповнення буфера у стеку розглянуто в [15, 60].