Анализ уязвимости переполнения буфера

Анализ уязвимости переполнения буфера

Сергеев, Р. А. Анализ уязвимости переполнения буфера / Р. А. Сергеев. — Текст : непосредственный // Молодой ученый. — 2017. — № 4 (138). — С. 181-185. — URL: https://moluch.ru/archive/138/38783/ (дата обращения: 06.07.2022).

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

Ключевые слова: компьютерная безопасность, переполнение буфера, эксплойты

Уязвимости переполнения буфера.

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

Переполнение буфера в стеке.

Рассмотрим простейшую программу на языке C (рисунок 1).

Рис. 1. Уязвимая программа на языке C

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

Рассмотрим, как выглядит переполнение со стороны отладчика. Снимки экрана с программой, запушенной под отладчиком Windbg, показаны на рисунках 2 и 3.

Рис. 2. Начало функции display и состояние стека в момент входа

Рис. 3. Состояние стека к моменту исполнения инструкции ret

На рисунке 2 (до команды отладчика dd esp) представлен дизассемблированный код начала функции display. Видно, что по адресу 0040134d происходит выполнение команды lea, которая в данном случае загружает в регистр eax указатель на область памяти в стеке. Далее этот указатель используется в качестве параметра строки-приемника функции strcpy, которая получает управление после исполнения инструкции call по адресу 00401353 (через прыжок по адресу 00401с00).

На рисунках 2 и 3 показаны два изображения состояния стековой области памяти, в момент входа в функцию display и перед самым выходом — исполнением инструкции ret. В данном случае программе передавалась строка, состоящая из 12h символов «A», четырех символов «B» и четырех символов «C». Хорошо видно, что важная управляющая информация — адрес возврата из функции — к моменту исполнения инструкции ret оказывается перезаписанной значением 43434343. Данное значение представляет собой четыре байта с ascii кодом символа «C». Таким образом, мы получаем здесь классическое переполнение буфера в стеке. Поскольку появляется возможность контролировать адрес возврата, то становится вероятным появление функциональности в уязвимой программе, которая не была запланирована ее разработчиком.

Переполнение буфера не в стеке.

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

Другим типом уязвимости переполнения является переполнение буфера, выделенного в куче [1, с. 119]. Куча — специальная область памяти, которую программа способна запрашивать динамически во время выполнения. Кроме того, что сами блоки памяти могут содержать важную для программы информацию, в выделенных из кучи участках содержатся служебные данные, перезапись которых может спровоцировать в конечном итоге выполнение произвольного кода.

На некоторых системах [2, с. 291] при наличии переполнения используются особенности алгоритма управления памятью из кучи. Все свободные куски памяти (области, освобожденные с помощью функции free()) объединяются в двухсвязные списки. На рисунке 4 схематично показан фрагмент одного из таких списков.

Рис. 4. Двухсвязный список свободных блоков кучи

В заголовках свободных блоков в списке содержится информация о размере блока (size), размере предыдущего блока (prev_size) в случаях, если он свободный, указатель на следующий блок (fd), указатель на предыдущий блок (bk). Если при освобождении блока он граничит со свободным куском, то освобождаемый кусок будет слит с этим свободным куском. При этом возникает необходимость удаления свободного блока из двухсвязного списка, для этого функция free() вызывает макрос unlink(). Этот макрос имеет следующий вид:

#define unlink(P, BK, FD)

📎📎📎📎📎📎📎📎📎📎