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

5.4.2. Переповнення буфера у статичній або динамічній пам'яті

Буфер не завжди розміщують у стеку. Програмісти, які знають про небезпеку «зривання стека», можуть спробувати виправити ситуацію, оголосивши буфер у статичній або динамічній пам'яті. Для наведеного вище фрагмента коду функції test_function () рядок коду

char buff[5];

у першому варіанті достатньо замінити рядком

static char buff[5];

а у другому — рядком

buff = (char *) malloc (5);

що справді вилучить буфери зі стека.

Але проблеми на цьому не вичерпуються [60]. Оскільки буфер оголошено не у стеку, порушників позбавляють можливості підмінити адресу повернення із функції. Щоправда, в них тоді з'являється інша можливість: здійснивши перепов­нення буфера, модифікувати дані, які знаходяться в адресному просторі програми, що виконується. Поряд з уразливим до переповнення буфером можуть знаходи­тися покажчики на функції та дані структур для функцій longjmp (), модифіку­вавши які, також можна викликати виконання власного коду. Крім того, поряд із буфером у статичній або динамічній пам'яті можуть знаходитися змінні, після мо­дифікування яких з'являється можливість викликати виконання функцій поруш­ника, навіть не передаючи керування, а саме: імена файлів, паролі та ідентифі­катори процесів (PID), користувачів (UID), груп (GID) тощо.

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

5.4.3. Помилка переповнення в один байт

Розглянемо специфічну помилку переповнення буфера, менш помітну, ніж роз­глянуті вище, але теж здатну викликати неприємності. Причина наявності по­милки переповнення в один байт полягає в особливостях форматів подання ряд­ків символів. У більшості форматів рядок займає у пам'яті на 1 байт більше, ніж потрібно для розміщення всіх його символів. У деяких мовах програмування ря­док завершується символом NULL, який не враховується для визначення його довжини. Старі функції MS-DOS передбачали роботу з рядками, які завершува-

лися символом $. А в мові Паскаль нульовий байт було відведено для значення довжини рядка.

Повернімося до прикладу функції test_function () (див. рис. 5.4). У функції оголошено буфер buff довжиною у 5 байт, куди можна помістити рядок із максимальною довжиною у 4 символи. Як було показано вище, для запобігання переповненню буфера доцільно передбачити перевірку довжини рядка. І якщо програміст припуститься помилки переповнення в один байт (тобто дозволить максимальну довжину рядка у 5 байт), то помилка переповнення буфера виникатиме тоді і лише тоді, коли довжина рядка дорівнює 5.

Оскільки логіка роботи функції під час аналізу вихідного тексту виглядатиме

абсолютно правильною, таку помилку помітити важко, якщо не шукати її спеціально. Звісно, ця помилка не дає змоги передати керування, позаяк змінюється (точніше, обнуляється) лише 1 байт, розташований безпосередньо за виділеним буфером. У наведеному прикладі це змінна b. Але цілком імовірно, що вона матиме

суттєве значення для логіки функціонування програми. Наприклад, значення цієї змінної може встановлювати рівень привілеїв користувача у системі, причому її нульове значення відповідає рівню суперкористувача.

Ще одна особливість полягає в тому, що різні компілятори можуть змінювати порядок розміщення локальних змінних у стеку. Тоді замість b у цій позиції опиниться змінна a. На практиці це виглядає так: інколи після введення певного рядка програма діє некоректно, хоча після оброблення іншим компілятором на тих самих вхідних даних діє цілком коректно або ж демонструє зовсім іншу помилку.