Защита от переполнения буфера
Защита от переполнения буфера — это любой из различных методов, используемых при разработке программного обеспечения для повышения безопасности исполняемых программ путем обнаружения переполнения буфера в переменных, выделенных в стеке , и предотвращения того, чтобы они вызывали неправильное поведение программы или превращались в серьезные безопасности уязвимости . Переполнение буфера стека происходит, когда программа записывает по адресу памяти в стеке вызовов программы за пределами предполагаемой структуры данных, которая обычно представляет собой буфер фиксированной длины. Ошибки переполнения буфера стека возникают, когда программа записывает в буфер, расположенный в стеке, больше данных, чем фактически выделено для этого буфера. Это почти всегда приводит к повреждению соседних данных в стеке, что может привести к сбоям программы, некорректной работе или проблемам безопасности.
Обычно защита от переполнения буфера изменяет организацию данных, выделенных в стеке, поэтому она включает канареечное значение, которое при уничтожении в результате переполнения буфера стека показывает, что предшествующий ему буфер в памяти был переполнен. Проверив значение canary, можно прервать выполнение затронутой программы, предотвращая ее некорректное поведение или позволяя злоумышленнику получить над ней контроль. Другие методы защиты от переполнения буфера включают проверку границ , которая проверяет доступы к каждому выделенному блоку памяти, чтобы они не могли выйти за пределы фактически выделенного пространства, и тегирование , которое гарантирует, что память, выделенная для хранения данных, не может содержать исполняемый код.
Переполнение буфера, выделенного в стеке, с большей вероятностью повлияет на выполнение программы, чем переполнение буфера в куче , поскольку стек содержит адреса возврата для всех активных вызовов функций. Однако аналогичные средства защиты, зависящие от реализации, также существуют и против переполнения кучи.
Существует несколько реализаций защиты от переполнения буфера, в том числе для GNU Compiler Collection , LLVM , Microsoft Visual Studio и других компиляторов.
Обзор
[ редактировать ]программы Переполнение буфера стека происходит, когда программа записывает по адресу памяти в стеке вызовов за пределами предполагаемой структуры данных, которая обычно представляет собой буфер фиксированной длины. Ошибки переполнения буфера стека возникают, когда программа записывает в буфер, расположенный в стеке, больше данных, чем фактически выделено для этого буфера. Это почти всегда приводит к повреждению соседних данных в стеке, а в случаях, когда переполнение было вызвано по ошибке, часто приводит к сбою программы или ее неправильной работе. Переполнение буфера стека — это тип более общей неисправности программирования, известной как переполнение буфера (или переполнение буфера). Переполнение буфера в стеке с большей вероятностью приведет к срыву выполнения программы, чем переполнение буфера в куче, поскольку стек содержит адреса возврата для всех активных вызовов функций. [1]
Переполнение буфера стека может быть вызвано намеренно в рамках атаки, известной как разрушение стека . Если затронутая программа работает с особыми привилегиями или принимает данные от ненадежных сетевых узлов (например, общедоступного веб-сервера ), то ошибка представляет собой потенциальную уязвимость безопасности, которая позволяет злоумышленнику внедрить исполняемый код в работающую программу и получить доступ к ней. контроль процесса. Это один из старейших и наиболее надежных способов получения злоумышленниками несанкционированного доступа к компьютеру. [2]
Обычно защита от переполнения буфера изменяет организацию данных в кадре стека , вызова функции включив в нее «канареечное» значение, которое при уничтожении показывает, что предшествующий ему буфер в памяти был переполнен. Это дает преимущество в предотвращении целого класса атак. По мнению некоторых исследователей, [3] влияние этих методов на производительность незначительно.
Защита от разрушения стека не способна защитить от определенных форм атак. Например, он не может защитить от переполнения буфера в куче. Не существует разумного способа изменить расположение данных внутри структуры ; ожидается, что структуры между модулями будут одинаковыми, особенно с общими библиотеками. Любые данные в структуре после буфера невозможно защитить канарейками; таким образом, программисты должны быть очень осторожны с тем, как они организуют свои переменные и используют их структуры.
Канарские острова
[ редактировать ]Канарейки или канареечные слова или файлы cookie стека — это известные значения, которые помещаются между буфером и управляющими данными в стеке для отслеживания переполнения буфера. Когда буфер переполняется, первыми данными, которые будут повреждены, обычно являются канарейки, и поэтому неудачная проверка канареечных данных приведет к предупреждению о переполнении, которое затем можно обработать, например, путем признания поврежденных данных недействительными. Канареечное значение не следует путать со сторожевым значением .
Эта терминология является отсылкой к исторической практике использования канареек на угольных шахтах , поскольку они будут подвергаться воздействию токсичных газов раньше, чем шахтеры, обеспечивая тем самым систему биологического предупреждения. Канарейки также известны как стековые файлы cookie , которые призваны вызывать образ «сломанного файла cookie», когда значение повреждено.
Используются три типа канареек: терминатор , случайный и случайный XOR . Текущие версии StackGuard поддерживают все три, а ProPolice поддерживает терминатора и случайных канарейок.
Канарейки-терминаторы
[ редактировать ]Канарейки-терминаторы используют наблюдение о том, что большинство атак на переполнение буфера основаны на определенных строковых операциях, которые заканчиваются на терминаторах строки. Реакцией на это наблюдение является то, что канарейки состоят из нулевых терминаторов CR , LF и FF . В результате злоумышленник должен написать нулевой символ перед записью обратного адреса, чтобы не изменить канарейку. Это предотвращает атаки с использованием strcpy()
и другие методы, которые возвращаются при копировании нулевого символа, в то время как нежелательным результатом является то, что канарейка известна. Даже при наличии защиты злоумышленник потенциально может перезаписать canary с его известным значением и управлять информацией с несовпадающими значениями, передавая таким образом код проверки canary, который выполняется незадолго до инструкции возврата из вызова конкретного процессора.
Случайные канарейки
[ редактировать ]Случайные канарейки генерируются случайным образом, обычно энтропии сбора демоном , чтобы злоумышленник не узнал их ценность. Обычно логически невозможно или правдоподобно прочитать канарейку на предмет эксплойта; канарейка — это безопасная величина, известная только тем, кому это необходимо знать — в данном случае код защиты от переполнения буфера.
Обычно случайная канарейка генерируется при инициализации программы и сохраняется в глобальной переменной. Эта переменная обычно дополняется несопоставленными страницами, поэтому попытка прочитать ее с помощью любых трюков, использующих ошибки для чтения из ОЗУ, вызывает ошибку сегментации, завершающую работу программы. Канарейку все равно можно прочитать, если злоумышленник знает, где она находится, или может заставить программу прочитать из стека.
Случайные канарейки XOR
[ редактировать ]Случайные канарейки XOR — это случайные канарейки, которые зашифрованы с помощью XOR с использованием всех или части управляющих данных. Таким образом, если канарейка или управляющие данные затерты, значение канарейки становится неправильным.
Случайные канарейки XOR имеют те же уязвимости, что и случайные канарейки, за исключением того, что метод получения канарейки «чтение из стека» немного сложнее. Злоумышленник должен получить канарейку, алгоритм и управляющие данные, чтобы заново сгенерировать исходную канарейку, необходимую для подмены защиты.
Кроме того, случайные канарейки XOR могут защитить от определенного типа атаки, включающей переполнение буфера в структуре в указатель с целью изменения указателя, чтобы он указывал на часть управляющих данных. Из-за кодировки XOR канарейка будет неправильной, если управляющие данные или возвращаемое значение будут изменены. Благодаря указателю управляющие данные или возвращаемое значение можно изменить без переполнения канарейки.
Хотя эти канарейки защищают управляющие данные от изменения затертыми указателями, они не защищают никакие другие данные или сами указатели. Особую проблему здесь представляют указатели на функции, поскольку они могут быть переполнены и могут выполнять шелл-код при вызове.
Проверка границ
[ редактировать ]Проверка границ — это метод, основанный на компиляторе, который добавляет информацию об границах во время выполнения для каждого выделенного блока памяти и сверяет все указатели с указателями во время выполнения. Для C и C++ проверка границ может выполняться во время вычисления указателя. [4] или во время разыменования. [5] [6] [7]
Реализации этого подхода используют либо центральный репозиторий, который описывает каждый выделенный блок памяти, [4] [5] [6] или жирные указатели , [7] которые содержат как указатель, так и дополнительные данные, описывающие область, на которую они указывают.
Тегирование
[ редактировать ]Тегирование [8] — это основанный на компиляторе или аппаратный (требующий тегированной архитектуры ) метод маркировки типа фрагмента данных в памяти, используемый в основном для проверки типов. Отмечая определенные области памяти как неисполняемые, он эффективно предотвращает содержание исполняемого кода в памяти, выделенной для хранения данных. Кроме того, определенные области памяти можно пометить как нераспределенные, чтобы предотвратить переполнение буфера.
Исторически тегирование использовалось для реализации языков программирования высокого уровня; [9] при соответствующей поддержке со стороны операционной системы тегирование также можно использовать для обнаружения переполнения буфера. [10] Примером может служить аппаратная функция бита NX , поддерживаемая процессорами Intel , AMD и ARM .
Реализации
[ редактировать ]Коллекция компиляторов GNU (GCC)
[ редактировать ]Защита от разрушения стека была впервые реализована компанией StackGuard в 1997 году и опубликована на симпозиуме USENIX Security Symposium в 1998 году . [11] StackGuard был представлен как набор исправлений для серверной части Intel x86 GCC 2.7. StackGuard поддерживался для дистрибутива Immunix Linux с 1998 по 2003 год и был расширен за счет реализаций терминатора, случайных и случайных канареек XOR. StackGuard было предложено для включения в GCC 3.x на саммите GCC 2003. [12] но этого так и не было достигнуто.
С 2001 по 2005 год IBM разработала патчи GCC для защиты от разрушения стека, известные как ProPolice . [13] Он усовершенствовал идею StackGuard, разместив буферы после локальных указателей и аргументов функций во фрейме стека. Это помогло избежать повреждения указателей и предотвращения доступа к произвольным ячейкам памяти.
Однако инженеры Red Hat выявили проблемы с ProPolice и в 2005 году повторно реализовали защиту от разрушения стека для включения в GCC 4.1. [14] [15] Эта работа представила -fstack-protector флаг, который защищает только некоторые уязвимые функции, а флаг -fstack-protector-all флаг, который защищает все функции независимо от того, нужны они им или нет. [16]
В 2012 году инженеры Google реализовали -fstack-protector-strong флаг, чтобы обеспечить лучший баланс между безопасностью и производительностью. [17] Этот флаг защищает больше видов уязвимых функций, чем -fstack-protector делает, но не каждую функцию, обеспечивая лучшую производительность, чем -fstack-protector-all. Он доступен в GCC начиная с версии 4.9. [18]
Все пакеты Fedora скомпилированы с помощью -fstack-protector начиная с Fedora Core 5, и -fstack-protector-strong начиная с Федоры 20. [19] [20] Большинство пакетов в Ubuntu скомпилированы с помощью -fstack-protector с 6.10. [21] Каждый пакет Arch Linux скомпилирован с -fstack-protector с 2011 года. [22] Все пакеты Arch Linux, созданные с 4 мая 2014 г., используют -fstack-protector-strong. [23] Защита стека используется только для некоторых пакетов в Debian . [24] и только для базовой системы FreeBSD, начиная с версии 8.0. [25] Защита стека является стандартной в некоторых операционных системах, включая OpenBSD . [26] Закаленный Генту [27] и DragonFly BSD . [ нужна ссылка ]
StackGuard и ProPolice не могут защитить от переполнения в автоматически выделяемых структурах, которые переполняются указателями на функции. ProPolice, по крайней мере, изменит порядок выделения, чтобы такие структуры располагались перед указателями на функции. отдельный механизм защиты указателей . В PointGuard предложен [28] и доступен в Microsoft Windows. [29]
Майкрософт Визуал Студия
[ редактировать ]Компилятор Microsoft реализует защиту от переполнения буфера, начиная с версии 2003, посредством /GS , который включен по умолчанию, начиная с версии 2005. Переключатель командной строки [30] С использованием /GS- отключает защиту.
IBM-компилятор
[ редактировать ]Защита от разрушения стека может быть включена флагом компилятора. -qstackprotect
. [31]
Кланг/ LLVM
[ редактировать ]Clang поддерживает то же самое -fstack-protector варианты как GCC [32] и более сильный «безопасный стек» ( -fsanitize=safe-stack ) с таким же низким влиянием на производительность. [33] В Clang также есть три детектора переполнения буфера, а именно AddressSanitizer ( -fsanitize=address
), [6] УБСан ( -fsanitize=bounds
), [34] и неофициальный SafeCode (последнее обновление для LLVM 3.0). [35]
Эти системы имеют разные компромиссы с точки зрения снижения производительности, затрат памяти и классов обнаруженных ошибок. Защита стека является стандартной в некоторых операционных системах, включая OpenBSD . [36]
Интел-компилятор
[ редактировать ]Компилятор Intel C и C++ поддерживает защиту от разрушения стека с помощью параметров, аналогичных тем, которые предоставляются GCC и Microsoft Visual Studio. [37]
Отказоустойчивый C
[ редактировать ]Отказоустойчивый C [7] — это компилятор ANSI C с открытым исходным кодом, безопасный для памяти, который выполняет проверку границ на основе толстых указателей и объектно-ориентированного доступа к памяти. [38]
StackGhost (аппаратный)
[ редактировать ]StackGhost, изобретенный Майком Франценом , представляет собой простую настройку процедур заполнения/заполнения окна реестра, которая значительно усложняет эксплуатацию переполнения буфера. Он использует уникальную аппаратную функцию архитектуры Sun Microsystems SPARC (а именно: отложенное заполнение/заполнение окна внутрикадрового регистра в стеке) для обнаружения изменений указателей возврата (обычный способ взлома путей выполнения эксплойтами прозрачного и автоматического ). защита всех приложений без необходимости модификации двоичных файлов или исходного кода. Влияние на производительность незначительно, менее одного процента. Возникшие проблемы с GDB были решены Марком Кеттенисом два года спустя, что позволило включить эту функцию. После этого события код StackGhost был интегрирован (и оптимизирован) в OpenBSD /SPARC.
См. также
[ редактировать ]- Целостность потока управления
- Рандомизация макета адресного пространства
- Исполняемая космическая защита
- Отладчик памяти
- Статический анализ кода
Ссылки
[ редактировать ]- ^ Фитен, Уильям Л.; Сикорд, Роберт (27 марта 2007 г.). «ВТ-МБ. Нарушение границ памяти» . СЕРТ США .
- ^ Леви, Элиас (8 ноября 1996 г.). «Разбиваем стек ради удовольствия и прибыли» . Фрак . 7 (49): 14.
- ^ «Переполнение буфера: атаки и защита от уязвимости десятилетия *» (PDF) . Архивировано из оригинала (PDF) 9 марта 2013 г.
- ^ Jump up to: а б «Проверка границ для C» . Док.ic.ac.uk. Архивировано из оригинала 26 марта 2016 г. Проверено 27 апреля 2014 г.
- ^ Jump up to: а б «SAFECode: безопасная виртуальная архитектура» . Sva.cs.illinois.edu. 12 августа 2009 г. Проверено 27 апреля 2014 г.
- ^ Jump up to: а б с «Google/санитайзеры» . 19 июня 2021 г.
- ^ Jump up to: а б с «Отказоустойчивый C: верхняя страница» . Staff.aist.go.jp. 07.05.2013. Архивировано из оригинала 7 июля 2016 г. Проверено 27 апреля 2014 г.
- ^ «Вторник, 5 апреля 2005 г.» (PDF) . Feustel.us . Архивировано из оригинала (PDF) 23 июня 2016 года . Проверено 17 сентября 2016 г.
- ^ Стенкисте, Питер; Хеннесси, Джон (1987). «Теги и проверка типов в LISP: аппаратный и программный подходы» . Обзор операционных систем ACM Sigops . 21 (4). АКМ: 50–59. дои : 10.1145/36204.36183 .
- ^ «Обзор безопасности MCP корпоративных серверов ClearPath» (PDF) . Public.support.unisys.com. Архивировано из оригинала (PDF) 24 января 2013 г. Проверено 27 апреля 2014 г.
- ^ «Документы - 7-й симпозиум по безопасности USENIX, 1998 г.» . Usenix.org. 12 апреля 2002 г. Проверено 27 апреля 2014 г.
- ^ «Материалы саммита разработчиков GCC» (PDF) . Май 2003 г. Архивировано из оригинала 15 июля 2004 г. Проверено 17 сентября 2016 г.
{{cite web}}
: CS1 maint: bot: исходный статус URL неизвестен ( ссылка ) - ^ «Расширение GCC для защиты приложений от атак, разрушающих стек» . Research.ibm.com . Проверено 27 апреля 2014 г.
- ^ «Серия выпусков GCC 4.1 — изменения, новые функции и исправления — Проект GNU — Фонд свободного программного обеспечения (FSF)» . Gcc.gnu.org . Проверено 27 апреля 2014 г.
- ^ «Ричард Хендерсон — [rfc] повторная реализация средства защиты от разрушения стека IBM» . Gcc.gnu.org . Проверено 27 апреля 2014 г.
- ^ «Параметры оптимизации — использование коллекции компиляторов GNU (GCC)» . Gcc.gnu.org . Проверено 27 апреля 2014 г.
- ^ "Han Shen(ææ) - [PATCH] Добавить новую опцию "-fstack-protector-strong" (патч/документ внутри)" . Gcc.gnu.org. 14 июня 2012 г. Проверено 27 апреля 2014 г.
- ^ Эдж, Джейк (5 февраля 2014 г.). « Надежная» защита стека для GCC» . Еженедельные новости Linux . Проверено 28 ноября 2014 г.
Он появился в GCC 4.9.
- ^ «Функции безопасности» . Проект Федора. 11 декабря 2013 г. Проверено 27 апреля 2014 г.
- ^ "#1128 (переключение с "-fstack-protector" на "-fstack-protector-strong" в Fedora 20) – FESCo" . Fedorahosted.org . Проверено 27 апреля 2014 г.
- ^ «Безопасность/функции — Ubuntu Wiki» . Wiki.ubuntu.com . Проверено 27 апреля 2014 г.
- ^ «FS#18864: рассмотрите возможность включения защиты от разрушения стека GCC (ProPolice, SSP) для всех пакетов» . Багс.archlinux.org . Проверено 27 апреля 2014 г.
- ^ «svntogit/packages.git — Git-клон репозитория пакетов» . Архивировано из оригинала 18 июля 2014 года.
- ^ «Статистика усиления безопасности Debian» . Outflux.net. Архивировано из оригинала 28 апреля 2014 г. Проверено 27 апреля 2014 г.
- ^ «Примечания к выпуску FreeBSD 8.0-RELEASE» . Freebsd.org. 13 ноября 2013 г. Проверено 27 апреля 2014 г.
- ^ «Страница руководства OpenBSD по gcc-local(1)» .
gcc поставляется с расширением защиты стека ProPolice , которое включено по умолчанию.
- ^ «Hardened/Toolchain — Gentoo Wiki» . 31 июля 2016 г.
Усиленный GCC Gentoo по умолчанию включает защиту стека, если только не было явного запроса не делать этого.
- ^ «12-й симпозиум по безопасности USENIX — Технический документ» .
- ^ «Блоги MSDN — получайте самую свежую информацию, идеи, объявления и новости от экспертов и разработчиков Microsoft в блогах MSDN» . 6 августа 2021 г.
- ^ «/GS (Проверка безопасности буфера) (C++)» . msdn.microsoft.com . Проверено 27 апреля 2014 г.
- ^ «qstackprotect» . Публикация.boulder.ibm.com . Проверено 27 апреля 2014 г.
- ^ «Список рассылки Clang» . Clang.llvm.org. 28 апреля 2017 года . Проверено 16 ноября 2022 г.
- ^ «SafeStack — документация Clang 17.0.0git» . clang.llvm.org .
- ^ «Руководство пользователя компилятора Clang — документация Clang 3.5» . Clang.llvm.org . Проверено 27 апреля 2014 г.
- ^ «БЕЗОПАСНЫЙ код» . Safecode.cs.illinois.edu . Проверено 27 апреля 2014 г.
- ^ «Страница руководства OpenBSD clang-local(1)» .
В clang защита стека включена по умолчанию, что эквивалентно опции -fstack-protector-strong в других системах.
- ^ «Пользовательское и справочное руководство для компилятора Intel C++ 15.0: fstack-security-check, GS» . программное обеспечение.intel.com . Проверено 13 февраля 2015 г.
- ^ "тезис.dvi" (PDF) . Staff.aist.go.jp . Проверено 17 сентября 2016 г.
Внешние ссылки
[ редактировать ]- Протоколы саммита GCC 2003 (PDF)
- Разбиваем стек ради удовольствия и прибыли, Алеф Один
- Официальный дом ProPolice
- Домашняя страница Immunix StackGuard
- Оригинальный документ StackGuard в USENIX Security 1998 г.
- StackGhost: аппаратная защита стека
- Реализация прополиса FreeBSD 5.4 и 6.2
- Четыре разных трюка для обхода защиты StackShield и StackGuard
- Защитник от разрушения стека