Является ли переполнение стека дырой безопасности?

Примечание: этот вопрос относится к переполнениям стека (подумайте о бесконечной рекурсии), а не к переполнениям буфера.

Если я пишу правильную программу, но она принимает вход из Интернета, который определяет уровень рекурсии в рекурсивной функции, которую она вызывает, это потенциально достаточно, чтобы позволить кому-то скомпрометировать машину?

Я знаю, что кто-то может разбить процесс, вызвав переполнение стека, но могут ли они вводить код? Или все-таки с время выполнения обнаруживает условие переполнения стека и отменяет чисто?

просто любопытно...

5 ответов


Быстрое Повышения Квалификации

во-первых, вам нужно понять, что основными единицами защиты в современных ОС являются процесс и страница памяти. Процессы-это Домены защиты памяти; это уровень, на котором ОС применяет политику безопасности, и поэтому они сильно соответствуют запущенной программе. (Где они этого не делают, это либо потому, что программа работает в нескольких процессах, либо потому, что программа совместно используется в какой-то структуре; в последнем случае потенциал быть "интересным для безопасности", но это "другая история".) Страницы виртуальной памяти-это уровень, на котором аппаратное обеспечение применяет правила безопасности; каждая страница в памяти процесса имеет атрибуты, определяющие, что процесс может делать со страницей: может ли он читать страницу, может ли он писать на нее и может ли он выполнять программный код на ней (хотя третий атрибут используется гораздо реже, чем, возможно, он должен быть). Скомпилированный программный код отображается в память на страницы это и читаемый и исполняемый файл, но не записываемый, тогда как стек должен быть читаемым и записываемым, но не исполняемым. большинство страниц памяти не являются читаемыми, записываемыми или исполняемыми вообще; ОС позволяет процессу использовать столько страниц, сколько он явно запрашивает, и это то, что библиотеки выделения памяти (malloc() et al.) управление для вас.

анализ

при условии, что каждый кадр стека меньше, чем страница памяти[1] так, что как программа продвигается через стек, она пишет на каждую страницу, ОС (т. е. привилегированная часть среды выполнения) может, по крайней мере, в принципе надежно обнаружить переполнение стека и завершить программу, если это произойдет. В принципе, все, что происходит с этим обнаружением, - это то, что в конце стека есть страница, на которую программа не может писать; если программа пытается записать в нее, аппаратное обеспечение управления памятью ловит ее, и ОС получает шанс вмешаться.

потенциальные проблемы с этим приходят, если ОС может быть обманут, не устанавливая такую страницу или если кадры стека могут стать настолько большими и скудно написанными, что страница guard перепрыгивается. (Сохранение большего количества страниц защиты поможет предотвратить второй случай с небольшими затратами; принудительное распределение стека переменного размера – например,alloca() - всегда писать в пространство, которое они выделяют, прежде чем возвращать управление программе, и поэтому обнаруживать разбитый стек, предотвратит первый с некоторой стоимостью с точки зрения скорости, хотя записи могут быть достаточно разреженными, чтобы сохранить стоимость довольно небольшой.)

последствия

каковы последствия этого? Ну, ОС должна делать правильные вещи с управлением памятью. (Ссылка@Michael иллюстрирует, что может произойти, когда она ошибается.) Но также опасно позволять злоумышленнику определять размеры выделения памяти, где вы не заставляете запись на все выделение немедленно;alloca и C99 переменной-размера массива особую угрозу. Более того, я был бы более подозрителен к коду c++, поскольку он имеет тенденцию делать гораздо больше распределения памяти на основе стека; это может быть нормально, но есть больший потенциал для того, чтобы все пошло не так.

лично я предпочитаю держать размеры стека и стека небольшого размера в любом случае и делать все разного размера отчислений в куче. Отчасти это наследие работы над некоторыми типами встроенных систем и с кодом, который использует очень большое количество потоков, но делает защиту от стека атаки переполнения намного проще; ОС может надежно поймать их, и все, что атакующий имеет, - это отказ в обслуживании (раздражающий, но редко фатальный). Я не знаю, является ли это решением для всех программистов.


[1] типичные размеры страницы: 4kB на 32-разрядных системах, 16kB на 64-разрядных системах. Проверьте системную документацию на предмет того, что находится в вашей среде.


большинство систем (например, Windows) выходят, когда стек переполнен. Я не думаю, что вы, вероятно, увидите здесь проблему безопасности. По крайней мере, не проблема с повышением привилегий. Могут возникнуть проблемы с отказом в обслуживании.


нет универсального правильного ответа... в какой-то системе стек может расти вниз или вверх, чтобы перезаписать другую память, используемую программой (или другой программой или ОС), но на любой хорошо спроектированной, смутно осознающей безопасность ОС (Linux, любой общий вариант UNIX, даже Windows) не будет потенциала эскалации прав. В некоторых системах с отключенными проверками размера стека адресное пространство может приблизиться к свободному размеру виртуальной памяти или превысить его, что отрицательно скажется на исчерпании памяти влияет или даже сбивает всю машину, а не только процесс, но на хороших Осах по умолчанию есть ограничение на это (например, команды limit / ulimit Linux).

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


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

Не падай в эту яму.

редактировать

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