Переполнение
буфера (buffer overflows) — название самой
распространенной уязвимости в
области безопасности программного
обеспечения. Первая атака с
применением данной уязвимости
использовалась в вирусе-черве
Морриса в 1988 году. С тех пор их число
увеличивается с каждым годом. В
настоящее время можно говорить, что
уязвимости, связанные с переполнение
буфера являются доминирующими при
удаленных атаках, где обычный
пользователь сети получает
частичный или полный контроль над
атакуемым хостом. Анализ атак и
обнаруженных уязвимостей последних
лет показывает, что данная проблема
является первостепенной. Так,
например, 9 из 13 выпусков CERT (Computer
Emergency Response Team site) в 1998 году и по
крайней мере половина выпусков 1999
года связаны с переполнением буфера
[1]. Информационный обзор популярного
списка рассылки Bugtraq показывает, что
примерно 2/3 респондентов считает
переполнение буфера основной
причиной нарушения сетевой
безопасности [2]. Отметим, что
переполнение буфера присуще также
программному обеспечению ряда
аппаратных средств. Примером может
служить уязвимость принтера HP LaserJet
4500 [20]. Очевидно, что эффективное
решение данной проблемы позволит
исключить большую долю самых
серьезных угроз компьютерной
безопасности.

Основа
атак с использованием этой
уязвимости — принцип
функционирования операционных
систем, где программа получает
привилегии и права запустившего ее
пользователя или процесса. Таким
образом, менее привилегированный
пользователь или процесс, который
взаимодействует с данной программой
может использовать ее права в своих
целях. Штатные средства программного
обеспечения не позволяют выполнять
такие действия. Однако переполнение
буфера все же делает это возможным.
Использование данной уязвимости
подразумевает изменение хода
выполнения привилегированной
программы, например, запуск
командной оболочки с правами
администратора. Реализации атаки
требует решения двух подзадач.

  • Подготовка
    кода, который будет выполнятся в
    контексте привилегированной
    программы.

  • Изменение
    последовательности выполнения
    программы с передачей управления
    подготовленному коду.

Методы
решения этих двух подзадач могут
служить основой классификации атак,
связанных с переполнением буфера [3].

Рассмотрим
пути решения подзадачи подготовки
кода.

  • Подготавливаемый
    код представляет собой
    исполняемый машинные инструкции
    соответствующего процессора и
    может передаваться в программу в
    качестве ее параметра или команды.
    При этом параметр или команда
    сохраняется программой в
    отведенном для этого буфере. Буфер
    может находится в любой области
    памяти: в стеке (локальные,
    автоматические переменные), в
    динамически распределяемой памяти,
    в области статических данных.
    Например, программе, запрашивающей
    ввод строки, под видом строки может
    быть передан нужный атакующему код,
    которому в дальнейшем будет
    передано управление.

  • Нужный
    код не передается в программу, так
    как он уже присутствует в ней самой
    или в ее адресном пространстве и
    требуется лишь его параметризация.
    Например, подготовка параметра для
    функции запуска программы. В
    данном случае атакующему
    требуется изменить или
    сформировать нужный параметр (для
    нашего примера указатель на строку
    с названием программы), а не сам код.
    Параметр также может находится в
    любой области памяти.

  • Если
    параметризированный код уже
    присутствует в адресном
    пространстве программы, то
    подготовки кода не требуется.

Далее
рассмотрим способы передачи
управления подготовленному коду. В
основе этих способов лежит
переполнение буфера, т. е. блока
памяти, выделенного под переменную.
Переполнение возникает при
отсутствии проверки выхода за
границы буфера. Таким образом,
искажается содержимое других
переменных состояния и параметров
программы, которые входят в область
переполнения буфера. Типы искажаемых
объектов-переменных определяет
способ передачи управления коду
атакующего и могут быть следующими.

Искажение
адреса возврата из функции

Так
как вызову функции сопутствует
занесение адреса возврата в стек, то
при его подмене атакующим,
управление передается по заданному
им адресу. Здесь используется
переполнение буфера локальных
переменных функции, которые также
создаются в стеке. Простым примером
служит следующий фрагмент программы
на Си.

int
namelen (void) {

char
name[21];

gets(name);

return
strlen(name);

}

Из
примера видно, что при вводе имени
размером более 20 символов частью
строки будет замещен адрес возврата
из функции. Далее, при выполнении
инструкции возврата из подпрограммы,
управление будет передано по адресу,
который образуют соответствующие
позиции введенной строки и в обычной
ситуации будет получено сообщение об
ошибке операционной системы.
Описанный процесс изображен на
рисунке 1.

Рисунок
1. Схема атаки срыв стека

Такие
атаки на переполнение буфера
получили название атаки срыва
стека (stack smashing attack) [4, 5, 6, 7].

Искажение
указателя функции

В
данном случае атаке подвергаются
переменные, содержащие указатели на
функции. Эти переменные могут
располагаться в любой области памяти,
не только в стеке но и в области
динамически и статически выделяемых
данных. Атакующий организовывает
переполнение буфера, которое
искажает данные указатели, и далее
при вызове функций по этим
указателям управление передается
подготовленному коду. Приведем
пример уязвимости указателей в виде
следующего фрагмента программы на Си.

void
dummy(void) {

printf("Hello
world!n");

}

int
main(int argc, char **argv) {

void
(*dummyptr)();

char
buffer[100];

dummyptr=dummy;

strcpy(buffer,
argv[1]); // Уязвимость

(*dummyptr)();

}

Здесь
переполнение буфера buffer приводит к
подмене указателя dummyptr и
последующему изменению хода
выполнения программы.

Искажение
таблиц переходов

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

 

Искажение
указателей данных

Данный
способ не предусматривает явной
передачи управления подготовленному
коду, но предполагает, что передача
происходит на основании
оригинального алгоритма программы.
Другими словами, подготовленный код
запускается в ходе обычной, не
искаженной, последовательности
исполнения программы.

Рассмотрим
следующий фрагмент программы на C.

foo(char
* arg) {

char
* p = arg; // уязвимый указатель

char
a[40]; // переполняемый буфер

gets(a);
// применение gets() реализует
уязвимость

gets(p);
// искажение кода

}

Здесь
переполнение буфера a вызывает
подмену указателя p и последующую
запись строки по адресу искаженного
указателя. Вводимая строка содержит
код атакующего. Такая схема атаки
часто используется для
корректировки (patch) части кода
программы или кода динамических и
статических библиотек,
располагающихся в памяти по
фиксированным адресам. Например,
корректировка-подмена системных
функции выхода из программы или
запуска процесса.

Другой
пример атаки подобного рода —
искажение указателя кадра стека
локальных переменных (frame pointer overwrite
attack) [8]. Эта атака основана на
стандартных операциях пролога и
эпилога подпрограмм, в результате
чего подменяется указатель базы
кадра локальных переменных.

На
мой взгляд, комбинация всех методов
подготовки кода и целей переполнения
буфера (типа искажаемых структур)
определяет виды всех возможных атак
по переполнению буфера, что
позволяет их классифицировать.
Результат комбинации приведен в
следующей таблице.

 

Таблица
1. Классификация атак по переполнению
буфера

Подготовка
кода

Цель
переполнения

Внедрение
кода

Внедрение
параметров

Не
требуется

Искажение
адреса возврата из функции

Атака
срыв стека

Атака
срыв стека с
параметризацией

Атака
срыв стека с передачей
управления

Искажение
указателей функций

Атака
на указатели функций

Атака
на указатели функций с
параметризацией

Атака
на указатели функций с
передачей управления

Искажение
таблиц переходов

Атака
на таблицы переходов

Атака
на таблицы переходов с
параметризацией

Атака
на таблицы переходов с
передачей управления

Искажение
указателей данных

Атака
с искажением указателей данных

Атака
с искажением указателей данных
с параметризацией

Атака
с искажением указателей данных
с оригинальным кодом

 

Оставит комментарий