Книга: Программирование для Linux. Профессиональный подход
10.6.1. Переполнение буфера
10.6.1. Переполнение буфера
Почти псе основные Internet-демоны, включая демоны таких программ, как sendmail
, finger
, talk
и др., подвержены атакам типа переполнение буфера. О них следует обязательно помнить при написании программ, которые должны выполняться с правами пользователя root, а также программ, осуществляющих межзадачное взаимодействие или читающих файлы, которые не принадлежат пользователю, запустившему программу.
Суть атаки заключается в том, чтобы заставить программу выполнить код, который она не собиралась выполнять. Типичный механизм достижения этой цели — перезапись части стека программы. В стеке, помимо всего прочего, сохраняется адрес памяти, по которому программа передает управление после завершения текущей функции. Следовательно, если поместить код взлома в памяти, а затем изменить адрес возврата так. чтобы он указывал на этот код, то по завершении текущей функции программа начнет выполнять код хакера с правами текущего процесса. Если процесс принадлежит пользователю root
, последствия будут катастрофическими. Если атаке подвергся процесс другого пользователя, катастрофа наступит "только" для него (а также для любого пользователя, который работает с файлами пострадавшего).
Хуже всего обстоит дело с программами, которые работают в режиме демона и ожидают поступление запросов на подключение. Демоны обычно принадлежат пользователю root
. Если в программе есть описываемая "дыра", любой, кто сможет подключиться к этой программе, способен захватить контроль над компьютером, послав по сети "смертельную" последовательность данных. Программы, не работающие с сетью, гораздо безопаснее, так как их могут атаковать только пользователи, уже зарегистрировавшиеся в системе.
Старым версиям программ finger
, talk
и sendmail
присущ один общий недостаток: все они работают со строковым буфером фиксированной длины. Предельный размер строки предполагается по умолчанию, но ничто не мешает сетевым клиентам вводить строки, вызывающие переполнение буфера. В программах содержится примерно такой код, как показан ниже.
#include <stdio.h>
int main() {
/* Никто, будучи в здравом уме, не выбирает имя пользователя
длиной более 32 символов. Кроме того, я думаю, что в UNIX
допускаются только 8-символьные имена. Поэтому выделенного
буфера должно быть достаточно. */
char username[32];
/* Предлагаем пользователю ввести свое имя. */
printf("Enter your username: ");
/* Читаем введенную строку. */
gets(username);
/* Выполняем другие действия... */
return 0;
}
Комбинация 32-символьного буфера и функции gets()
делает возможным переполнение буфера. Функция gets()
читает вводимые данные до тех пор, пока не встретится символ новой строки, после чего помещает весь результат в массив username
. В комментариях к программе предполагается, что пользователи выбирают себе короткие имена, не превышающие в длину 32 символа. Но при написании защищенных программ необходимо помнить о существовании хакеров. В данном случае хакер может выбрать сколь угодно длинное имя. Локальные переменные, в частности username
, сохраняются в стеке, поэтому выход за пределы массива оборачивается тем, что в стек помещаются произвольные данные.
К счастью, предотвратить переполнение буфера относительно несложно. При чтении строк следует всегда пользоваться функцией наподобие getline()
, которая либо динамически выделяет буфер достаточной длины, либо прекращает принимать входные данные, когда буфер оказывается заполнен. Вот вариант выхода из положения:
char* username = getline(NULL, 0, stdin);
Функция getline()
автоматически вызывает функцию malloc()
, которая выделяет буфер для введенной строки и возвращает указатель на него. Естественно, следует не забыть вызвать функцию free()
, чтобы по окончании работы с буфером вернуть память системе.
Ситуация еще проще, если используется язык C++, где есть готовые строковые примитивы. В C++ ввод строки осуществляется так:
string username;
getline(cin, username);
Буфер строки username
удаляется автоматически, поэтому даже не придется вызывать функцию free()
.
Проблема переполнения буфера возникает при работе с любыми статическими массивами, а не только со строками. При написании безопасных программ следует тщательно проверять, не осуществляется ли запись в массив за его пределами.
- 14.2. Переполнение буфера
- 22.3.1. Переполнение буфера
- 2.11. Размеры буфера и ограничения
- Использование буфера обмена
- Переполнение переменной jiffies
- 10.5.1. Настройка буфера обмена
- 6.20.2 Использование буфера
- Переполнение и потеря значимости при обработке чисел с плавающей точкой
- Шутка №4 — очистка буфера обмена
- Листинг Б.3. (write-all.c) Запись буфера
- З.2.5. Утилита для просмотра и сохранения буфера обмена
- 3.1 ЗАГОЛОВКИ БУФЕРА