Введение
Аномалия в компьютерной безопасности и программировании
В программировании и информационной безопасности переполнение буфера – это аномалия, при которой программа записывает данные в буфер за пределы выделенной для него памяти, перезаписывая соседние области памяти. Буферы – это области памяти, предназначенные для хранения данных, часто при их перемещении из одного раздела программы в другой или между программами. Переполнение буфера часто может быть вызвано некорректными входными данными; если предполагается, что все входные данные будут меньше определенного размера, а буфер создан соответствующего размера, то аномальная транзакция, генерирующая больший объем данных, может привести к записи за пределы буфера. Если это перезаписывает соседние данные или исполняемый код, это может привести к непредсказуемому поведению программы, включая ошибки доступа к памяти, неверные результаты и сбои. Использование уязвимости переполнения буфера – хорошо известный метод атак. На многих системах структура памяти программы или системы в целом четко определена. Отправляя данные, предназначенные для вызова переполнения буфера, можно записывать в области, содержащие исполняемый код, и заменять его вредоносным кодом, или выборочно изменять данные, относящиеся к состоянию программы, что приведет к нежелательному поведению, не предусмотренному разработчиком. Буферы широко используются в коде операционных систем (ОС), поэтому возможны атаки, приводящие к повышению привилегий и неограниченному доступу к ресурсам компьютера. Знаменитый червь Морриса 1988 года использовал этот метод в качестве одной из своих техник. Языки программирования C и C++ часто связаны с переполнением буфера, поскольку они не предоставляют встроенной защиты от доступа или перезаписи данных в любой части памяти и не выполняют автоматическую проверку, находятся ли данные, записываемые в массив (встроенный тип буфера), в пределах границ этого массива. Проверка границ может предотвратить переполнение буфера, но требует дополнительного кода и времени обработки. Современные операционные системы используют различные методы для борьбы со злонамеренным переполнением буфера, в частности, рандомизацию структуры памяти или преднамеренное создание промежутков между буферами и поиск действий, осуществляющих запись в эти области ("канарейки").
Техническое описание
Переполнение буфера возникает, когда данные, записываемые в буфер, также повреждают данные в соседних ячейках памяти из-за недостаточной проверки границ. Это может произойти при копировании данных из одного буфера в другой без предварительной проверки, поместятся ли данные в буфер назначения.
Эксплуатация
Методы эксплуатации уязвимости переполнения буфера различаются в зависимости от архитектуры, операционной системы и области памяти. Например, эксплуатация в куче (используемой для динамически выделяемой памяти) существенно отличается от эксплуатации в стеке вызовов. В общем случае, эксплуатация кучи зависит от используемого менеджера кучи в целевой системе, а эксплуатация стека – от соглашения о вызовах, применяемого архитектурой и компилятором.
Эксплуатация на куче
Переполнение буфера, возникающее в области данных кучи, называется переполнением кучи и эксплуатируется способом, отличным от переполнения, основанного на стеке. Память в куче динамически выделяется приложением во время выполнения и обычно содержит данные программы. Эксплуатация выполняется путем намеренного искажения этих данных, чтобы заставить приложение перезаписать внутренние структуры, такие как указатели связанных списков. Классический метод эксплуатации переполнения кучи заключается в перезаписи информации, связанной с динамическим выделением памяти (например, метаданных malloc), и использовании полученного обмена указателями для перезаписи указателя на функцию программы. Уязвимость GDI+ в Microsoft при обработке JPEG-изображений является примером опасности, которую может представлять переполнение кучи.
Препятствия для эксплуатации
Манипулирование буфером, выполняемое до его чтения или исполнения, может привести к срыву попытки эксплуатации. Эти манипуляции могут снизить угрозу эксплуатации, но не исключить её полностью. Манипулирование может включать преобразование в верхний или нижний регистр, удаление метасимволов и фильтрацию неалфавитно-цифровых строк. Однако существуют способы обхода этих фильтров и манипуляций, такие как алфавитно-цифровой шелл-код, полиморфный код, самомодифицирующийся код и атаки типа "return-to-libc". Те же методы могут использоваться для обхода обнаружения системами обнаружения вторжений. В некоторых случаях, особенно когда код преобразуется в Unicode, раскрывшие информацию ошибочно представляли угрозу уязвимости как исключительно отказ в обслуживании, хотя на самом деле возможна удалённая эксплуатация с выполнением произвольного кода.
Практические особенности эксплуатации
В реальных атаках существует ряд сложностей, которые необходимо преодолеть для надежной работы эксплойтов. К этим факторам относятся нулевые байты в адресах, вариативность расположения шелл-кода, различия между окружениями и различные действующие средства защиты.
Техника сноубордов NOP
Снег NOP — старейшая и наиболее известная техника эксплуатации переполнения буфера стека. Язык C не предоставляет встроенной защиты от доступа или перезаписи данных в любой области памяти. В частности, он не проверяет, находятся ли данные, записываемые в буфер, в пределах границ этого буфера. Стандартные библиотеки C++ предоставляют множество способов безопасной буферизации данных, а библиотека стандартных шаблонов C++ (STL) предлагает контейнеры, которые могут выполнять проверку границ по запросу программиста при доступе к данным. Например, метод `at` вектора выполняет проверку границ и генерирует исключение «выход за пределы диапазона», если проверка не пройдена. Однако C++ ведёт себя как C, если проверка границ не вызывается явно. Существуют также методы предотвращения переполнения буфера для C. Языки со строгой типизацией, не допускающие прямого доступа к памяти, такие как COBOL, Java, Python и другие, в большинстве случаев предотвращают переполнение буфера. Хорошо разработанные и протестированные абстрактные библиотеки типов данных, централизованно и автоматически управляющие буферами, включая проверку границ, могут снизить вероятность и последствия переполнения буфера. Основными типами данных, подверженными переполнению буфера, являются строки и массивы. Следовательно, библиотеки, предотвращающие переполнение буфера для этих типов данных, могут обеспечить большую часть необходимой защиты. Однако неправильное использование этих безопасных библиотек может привести к переполнению буфера и другим уязвимостям, и, конечно, любая ошибка в библиотеке также является потенциальной уязвимостью. К «безопасным» библиотечным реализациям относятся «The Better String Library», Vstr и Erwin. C-библиотека операционной системы OpenBSD предоставляет функции `strlcpy` и `strlcat`, но они менее функциональны, чем полноценные безопасные библиотечные реализации. В сентябре 2007 года был опубликован технический отчёт 24731, подготовленный комитетом по стандартам C. В нём определен набор функций, основанных на строковых и IO-функциях стандартной библиотеки C, с добавлением параметров размера буфера. Однако эффективность этих функций в снижении риска переполнения буфера остаётся под вопросом. Они требуют вмешательства программиста при каждом вызове функции, что эквивалентно вмешательству, которое могло бы сделать аналогичные устаревшие функции стандартной библиотеки безопасными от переполнения буфера.
Languages that are strongly typed and do not allow direct memory access, such as COBOL, Java, Python, and others, prevent buffer overflow in most cases. Well written and tested abstract data type libraries that centralize and automatically perform buffer management, including bounds checking, can reduce the occurrence and impact of buffer overflows. The primary data types in languages in which buffer overflows are common are strings and arrays. Thus, libraries preventing buffer overflows in these data types can provide the vast majority of the necessary coverage. However, failure to use these safe libraries correctly can result in buffer overflows and other vulnerabilities, and naturally any bug in the library is also a potential vulnerability. "Safe" library implementations include "The Better String Library", Vstr and Erwin. The OpenBSD operating system's C library provides the strlcpy and strlcat functions, but these are more limited than full safe library implementations. In September 2007, Technical Report 24731, prepared by the C standards committee, was published. It specifies a set of functions that are based on the standard C library's string and IO functions, with additional buffer size parameters. However, the efficacy of these functions for reducing buffer overflows is disputable. They require programmer intervention on a per function call basis that is equivalent to intervention that could make the analogous older standard library functions buffer overflow safe.
Защита от переполнения буфера
Защита от переполнения буфера используется для обнаружения наиболее распространенных переполнений буфера путем проверки того, что стек не был изменен при возврате функции. Если стек был изменен, программа завершается с ошибкой сегментации. К таким системам относятся Libsafe, а также патчи StackGuard и ProPolice для gcc. Реализация Microsoft режима предотвращения выполнения данных (DEP) явно защищает указатель на структурированный обработчик исключений (SEH) от перезаписи. Более надежная защита стека достигается путем разделения стека на две части: одну для данных и одну для адресов возврата. Такое разделение присутствует в языке Forth, хотя изначально это не было обусловлено соображениями безопасности. Тем не менее, это не является полным решением проблемы переполнения буфера, поскольку могут быть перезаписаны и другие конфиденциальные данные, помимо адреса возврата. Этот тип защиты также не всегда точен, поскольку не обнаруживает все виды атак. Системы, такие как StackGuard, ориентированы на поведение атак, что делает их более эффективными и быстрыми по сравнению с системами проверки границ.
Защита указателя
Переполнение буфера работает за счет манипулирования указателями, включая хранимые адреса. PointGuard был предложен в качестве расширения компилятора для предотвращения надежного манипулирования указателями и адресами со стороны злоумышленников. Метод заключается в том, что компилятор добавляет код для автоматического XOR-шифрования указателей до и после их использования. Теоретически, поскольку злоумышленник не знает, какое значение будет использовано для шифрования и расшифровки указателя, он не может предсказать, на что будет указывать указатель, если он будет перезаписан новым значением. PointGuard так и не был выпущен, но Microsoft реализовала аналогичный подход, начиная с Windows XP SP2 и Windows Server 2003 SP1. Вместо реализации защиты указателей как автоматической функции, Microsoft добавила API-рутину, которую можно вызывать. Это позволяет добиться более высокой производительности (поскольку она не используется постоянно), но возлагает на программиста ответственность за определение необходимости ее использования. Поскольку XOR является линейным, злоумышленник может манипулировать зашифрованным указателем, перезаписывая только младшие байты адреса. Это может позволить атаке увенчаться успехом, если злоумышленник сможет предпринять несколько попыток эксплуатации или завершить атаку, заставив указатель указывать на одно из нескольких мест (например, на любое место в NOP-санях). Microsoft добавила случайную ротацию в свою схему шифрования для устранения этой уязвимости к частичным перезаписям.
Расположение адресной зоны
Рандомизация компоновки адресного пространства (ASLR) — это функция безопасности компьютера, которая заключается в случайном размещении ключевых областей данных, обычно включая базовый адрес исполняемого файла и положение библиотек, кучи и стека, в адресном пространстве процесса. Случайное распределение адресов виртуальной памяти, по которым можно найти функции и переменные, затрудняет, но не делает невозможным эксплуатацию переполнения буфера. Это также вынуждает злоумышленника адаптировать попытку эксплуатации к конкретной системе, что препятствует распространению интернет-червей. Аналогичным, но менее эффективным методом является перебазировка процессов и библиотек в виртуальном адресном пространстве.
Глубокая проверка пакетов
Использование глубокой инспекции пакетов (DPI) позволяет обнаруживать на периметре сети весьма примитивные удаленные попытки эксплуатации переполнения буфера, используя сигнатуры атак и эвристические методы. Эта техника может блокировать пакеты, содержащие сигнатуру известной атаки. Ранее она применялась в ситуациях, когда обнаруживалась длинная последовательность инструкций No Operation (известная как NOP-sled), а местоположение полезной нагрузки эксплойта незначительно варьировалось. Сканирование пакетов не является эффективным методом, поскольку способно предотвращать только известные атаки, и существует множество способов кодирования NOP-sled. Shellcode, используемый злоумышленниками, может быть представлен в буквенно-цифровом формате, быть метаморфическим или самомодифицирующимся, чтобы избежать обнаружения эвристическими сканерами пакетов и системами обнаружения вторжений.
Испытания
Проверка на переполнение буфера и устранение ошибок, приводящих к нему, помогает предотвратить переполнение буфера. Один из распространенных автоматизированных методов обнаружения таких ошибок — фаззинг. Тестирование граничных случаев также может выявить переполнение буфера, как и статический анализ. После обнаружения потенциального переполнения буфера его следует исправить. Это делает подход к тестированию полезным для разрабатываемого программного обеспечения, но менее полезным для устаревшего программного обеспечения, которое больше не обслуживается и не поддерживается.
История
Переполнение буфера было понято и частично публично задокументировано еще в 1972 году, когда в исследовании по планированию технологий компьютерной безопасности была описана эта техника: "Код, выполняющий эту функцию, не проверяет адреса источника и назначения должным образом, что позволяет пользователю перезаписывать части монитора. Это можно использовать для внедрения кода в монитор, который позволит пользователю захватить управление машиной". Сегодня монитор назывался бы ядром. Первая задокументированная враждебная эксплуатация переполнения буфера произошла в 1988 году. Это был один из эксплойтов, использованных червем Морриса для распространения по Интернету. Эксплуатируемой программой была служба Unix под названием finger. Позже, в 1995 году, Томас Лопатик независимо повторно открыл переполнение буфера и опубликовал свои результаты в списке рассылки безопасности Bugtraq. Год спустя, в 1996 году, Элиас Леви (также известный как Aleph One) опубликовал в журнале Phrack статью "Smashing the Stack for Fun and Profit" – пошаговое руководство по эксплуатации уязвимостей переполнения буфера в стеке. С тех пор по крайней мере два крупных интернет-червя использовали переполнение буфера для компрометации большого количества систем. В 2001 году червь Code Red использовал переполнение буфера в Microsoft Internet Information Services (IIS) 5.0, а в 2003 году червь SQL Slammer скомпрометировал машины, работающие под управлением Microsoft SQL Server 2000. В 2003 году переполнения буфера, обнаруженные в лицензионных играх для Xbox, были использованы для запуска нелицензионного программного обеспечения, включая самодельные игры, на консоли без необходимости аппаратных модификаций, известных как модчипы. PS2 Independence Exploit также использовал переполнение буфера для достижения аналогичного результата на PlayStation 2. Взлом Twilight достиг того же на Wii, используя переполнение буфера в The Legend of Zelda: Twilight Princess.