Переповнення буфера: причини, ефективні методи вирішення проблеми та необхідний захист

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

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

Визначення переповнення буфера

Визначення переповнення буфера

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

При застосуванні мов C і Assembler читання або запис таких розподілів не тягне за собою автоматичної перевірки кордонів. У зв`язку з чим, якщо виявлено переповнення стекового буфера в даному додатку, не існує перевірки на можливість приміщення числа байтів в розглянутий буфер. У таких випадках програма може "переповнити" його ємність. Це призводить до того, що дані, що записуються після наповнення, переписують вміст наступних адрес в стеку і зчитують Додаткові. Переповнення може статися ненавмисно через помилки користувача.

Буває, що воно викликано тим, що зловмисний суб`єкт посилає ретельно створений шкідливий введення в програму, яка потім намагається зберегти його в недостатній буфер. Якщо при цьому буде виявлено переповнення стекового буфера в даному додатку, надлишкові дані записуються в сусідній, де перезаписують будь-які наявні дані.

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

Використання переповнення буфера дозволяє зловмиснику контролювати або завершувати роботу процесу або змінювати його внутрішні змінні. Це порушення займає місце в топ-25 найбільш небезпечних програмних помилок світу (2009 CWE / SANS Top 25 Most Dangerous Programming Errors) і визначається як CWE - 120 в словнику перерахувань слабких системних місць. Незважаючи на те що вони добре вивчені, вони продовжують завдавати шкоди популярним програмам.

Простий вектор використання буфера

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

Простий вектор використання буфера

Дивлячись на код, видно, що перевірка меж не виконується. Якщо користувач вводить "можливо", то програма буде аварійно завершувати роботу, а не запитувати у нього відповідь, який записується в buffer незалежно від його довжини. У цьому прикладі, оскільки user answer є єдиною оголошеною змінною, наступні значення в стеку будуть значенням зворотної адреси або місцем у пам`яті, куди програма повернеться після виконання функції ask Question.

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

Якщо першим кроком для виявлення переповнення буфера у вихідному коді є розуміння того, як вони працюють, другим кроком є вивчення зовнішнього введення та маніпулювання буфером, то третім кроком буде необхідність знати, які функції піддаються цій вразливості та які можуть діяти як " червоні прапори». Функція gets відмінно підходить для запису за межами наданого їй buffer. Насправді ця якість поширюється на все сімейство пов`язаних можливостей, включаючи strcpy, strcmp та printf / sprintf, де б не використовувалася одна з цих функцій вразливості переповнення.

Видалення з кодової бази

Якщо виявлено переповнення буфера стека у вихідному коді, буде потрібно послідовне видалення їх з бази. Для цього треба бути знайомим з безпечними методами роботи. Найпростіший спосіб запобігти цим уразливостям-використовувати мову, яка їх не дозволяє. Мова C має ці вразливості завдяки прямому доступу до пам`яті та відсутності суворої типізації об`єктів. Мови, які не поділяють ці аспекти, зазвичай невразливі. Це Java, Python і .NET, поряд з іншими мовами та платформами, які не потребують спеціальних перевірок чи змін.

Звичайно, не завжди можливо повністю змінити мову розробки. В цьому випадку використовують безпечні методи для роботи з переповненням буфера команд. У випадку функцій обробки рядків було багато дискусій щодо того, які методи доступні, які безпечні у використанні та яких слід уникати. Функції strcpy та strcat копіюють рядок у буфер та додають вміст одного в інший. Ці два методи демонструють небезпечну поведінку, оскільки не перевіряють межі цільового buffer, і виконують запис за межами, якщо для цього достатньо байтів.

Альтернативний захист

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

Альтернативний захист

У цьому спрощеному прикладі видно небезпеку рядків, що не закінчуються нулем. Коли foo поміщається в normal buffer, він закінчується нулем, оскільки має додатковий простір. Це найкращий варіант розвитку подій. Якщо байти в переповнення буфера на стеку будуть в іншому символьному buffer або інший друкується рядку, функція друку Продовжити читання, поки не буде досягнутий завершальний символ цього рядка.

Недоліком є те, що мова C не забезпечує стандартну, безпечну альтернативу цим функціям. Проте є і позитив-доступність декількох реалізацій для конкретної платформи. OpenBSD надає strlcpy та strlcat, які працюють подібно до функцій strn, за винятком того, що вони скорочують рядок на один символ раніше, щоб звільнити місце для нульовий Термінатор.

Так само Microsoft надає власні безпечні реалізації часто використовуваних функцій обробки рядків: strcpy_s, strcat_s та sprintf_s.

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

Уразливості компіляції

Уразливості компіляції

У разі якщо небезпечна функція залишає відкриту можливість переповнення буфера C, то не все втрачено. При запуску програми компілятори часто створюють випадкові значення, відомі як канарки (canary), і поміщають їх в стек, тому становлять небезпеку. Перевірка значення канарки щодо її початкового значення може визначити, чи відбулося переповнення буфера Windows. Якщо значення було змінено, програма буде закрита або перейде в стан помилки, а не до потенційно зміненої адреси повернення.

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

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

Він працює шляхом рандомізації областей пам`яті структур, так що їх зміщення важче визначити. Якби цей захист існував наприкінці 1980 - х років, черв`яка Морріса можна було б утримати. Це пов`язано з тим, що він функціонував частково, заповнюючи буфер у протоколі UNIX finger кодом експлуатації, а потім переповнюючи його, щоб змінити адресу повернення та вказати на заповнений буфер.

ASLR і DEP ускладнюють точне визначення адреси, яку потрібно вказати, виконуючи цю область пам`яті повністю неробочою. Іноді вразливість прослизає через тріщини, відкриті для атаки переповнення буфера, незважаючи на наявність елементів керування на рівні розробки, компілятора або операційної системи.

Статичний аналіз покриття

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

Не можна припускати, що таке оновлення забезпечить достатнє охоплення. Організації або приватні особи можуть використовувати програмне забезпечення в системах з обмеженим доступом до Інтернету, які потребують ручного оновлення. Це означає, що новини про оновлення повинні бути поширені серед будь-яких адміністраторів, які можуть використовувати ПЗ, а патч повинен бути легкодоступний для завантаження. Створення і поширення виправлень виконують якомога ближче до виявлення уразливості, що забезпечує мінімізацію часу уразливості.

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

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

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

Виконання атаки через root

Помилки кодування зазвичай є причиною переповнення buffer. Поширені помилки при розробці додатків, які можуть привести до нього, включають в себе нездатність виділити досить великі буфери і відсутність механізму перевірки цих проблем. Такі помилки особливо проблематичні в мовах C / C++, які не мають вбудованого захисту від переповнення і часто є об`єктами атак переповнення буфера.

У деяких випадках зловмисник вводить шкідливий код у пам`ять, яка була пошкоджена через переповнення буфера стека. В інших випадках просто використовують переваги пошкодження сусідньої пам`яті. Наприклад, програма, яка запитує пароль користувача, щоб надати йому доступ до системи. У наведеному нижче коді правильний пароль надає привілеї root. Якщо пароль невірний, програма не надає користувачеві привілеї.

Програма не надає користувачеві привілеї

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

Атака тимчасової області зберігання

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

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

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

Стек - простір пам`яті, що використовується для зберігання користувальницького уведення. Таке переповнення частіше зустрічається у зловмисників, які використовують додатки.

Сучасні компілятори зазвичай надають можливість перевірки переповнення під час компіляції / посилання, але під час виконання досить важко перевірити цю проблему без будь-якого додаткового механізму захисту обробки винятків.

Виконання атаки через root

Варіанти роботи програми:

  1. Введення: 12345678 (8 байт), програма працює без збоїв.
  2. Введення: 123456789 (9 байт), з`явиться повідомлення "Помилка сегментації", програма завершується.

Уразливість існує через переповнення, якщо введення користувачем argv перевищує 8 байт. Для 32-бітної системи (4 байти) заповнюють пам`ять подвійним словом (32 біти). Розмір символу становить 1 байт, тому якщо запросити буфер з 5 байтами, система виділить 2 подвійних слова (8 байт). Ось чому при введенні більше 8 байт Buffer буде переповнений.

Подібні стандартні функції, які технічно менш вразливі, існують. Наприклад, strncpy (), strncat ()та memcpy (). Проблема з цими функціями полягає в тому, що відповідальність для визначення розміру буфера лежить на програмісті, а не на компіляторі.

Кожен програміст C / C++ повинен знати проблему перед початком кодування. Багато генеруються проблеми в більшості випадків можуть бути захищені від переповнення.

Небезпеки в C / C++

http://blogs.grammatech.com/eliminating-the-danger-of-uninitialized-variables

Користувачі C повинні уникати застосування небезпечних функцій, які не перевіряють межі, якщо вони не впевнені, що межі не будуть перевищені. Функції, яких слід уникати в більшості випадків, щоб забезпечити захист, включають функції strcpy. Їх слід замінити такими функціями, як strncpy. Слід уникати використання функції strlen, якщо користувач впевнений, що буде знайдено кінцевий символ NIL. Сімейство scanf (): scanf (3), fscanf (3), sscanf (3), vscanf (3), vsscanf (3) і vfscanf (3) - небезпечно для використання, його не застосовують для відправки даних в рядок без контролю максимальної довжини, "формат % s" є особливо поширеним збоєм.

Офіційно snprintf () не є стандартною функцією C у класифікації ISO 1990. Ці системи не захищають від переповнення буфера, вони просто викликають sprintf безпосередньо. Відомо, що поточна версія Linux snprintf працює належним чином, тобто фактично дотримується встановленої межі. Повернене значення snprintf () також змінюється.

Версія 2 специфікації Unix (sus) та стандарт C99 відрізняються тим, що повертає snprintf (). Деякі версії snprintf don`t гарантують, що рядок закінчиться в NIL, а якщо рядок занадто довгий, він взагалі не буде містити NIL. Бібліотека glib має g_snprintf () з послідовною семантикою повернення, завжди закінчується NIL і, що найголовніше, завжди враховує довжину буфера.

Переповнення буфера комунікаційного порту

Переповнення буфера комунікаційного порту

Іноді послідовний порт повідомляє про переповнення buffer. Ця проблема може бути викликана кількома факторами. До них відносяться швидкість комп`ютера, швидкість передачі даних, що використовуються, розмір послідовного порту FIFO та розмір пристрою FIFO, який передає дані на послідовний порт.

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

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

Налаштування VISA та Windows за замовчуванням для 16-байтового FIFO становлять 14 байт, залишаючи 2 байти у FIFO, коли пристрій намагається надіслати повідомлення від джерела. При більш високих швидкостях передачі на повільних комп`ютерах можливо отримати більше 4 байтів в момент, коли послідовний порт запитує процесор, посилаючи сигнал про припинення передачі.

Щоб вирішити проблему, коли виявлено переповнення стекового буфера в Windows 10, потрібно відкрити диспетчер пристроїв. Потім знайти COM-порт, для якого змінюють налаштування, і відкрити властивості. Далі натискають на вкладку "Додатково", з`явиться повзунок, яким змінюють розмір переповнення буфера обміну, щоб UART швидше включив управління потоком.

Значення за замовчуванням в більшості випадків досить. Однак якщо надходить помилка переповнення buffer, зменшують значення. Це призведе до того, що більше переривань буде надіслано процесору уповільнення байтів в UART.

Методи безпечної розробки

Методи безпечної розробки

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

Хмарна служба Veracode виявляє вразливості коду, такі як переповнення buffer, тому розробники усувають їх до того, як вони будуть використані. Унікальна в галузі запатентована технологія тестування безпеки бінарних статичних додатків (SAST) Veracode аналізує його, включаючи компоненти з відкритим вихідним кодом і сторонні, без необхідності доступу до нього.

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

SAST виявляє критичні вразливості, такі як введення SQL, міжсайтовий сценарій (XSS), помилка переповнення буфера, необроблені стани помилок та потенційні куточки. Крім того, двійкова технологія SAST надає корисну інформацію, яка визначає пріоритети залежно від серйозності та надає детальну інструкцію по виправленню.

Уразливість переповнення buffer існує вже майже 3 десятиліття, але вона все ще обтяжлива. Хакери по всьому світу продовжують вважати її своєю тактикою за замовчуванням через величезну кількість сприйнятливих веб-додатків. Розробники і програмісти витрачають величезні зусилля для боротьби з цим злом IT-технологій, придумуючи все нові і нові способи.

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

Статті на тему