Переполнение буфера: Как устроены дыры в безопасности: переполнение буфера / Хабр
Содержание
Как устроены дыры в безопасности: переполнение буфера / Хабр
Прим. переводчика: Это перевод статьи Питера Брайта (Peter Bright) «How security flaws work: The buffer overflow» о том, как работает переполнение буфера и как развивались уязвимости и методы защиты.
Беря своё начало с Червя Морриса (Morris Worm) 1988 года, эта проблема поразила всех, и Linux, и Windows.
Переполнение буфера (buffer overflow) давно известно в области компьютерной безопасности. Даже первый само-распространяющийся Интернет-червь — Червь Морриса 1988 года — использовал переполнение буфера в Unix-демоне finger для распространения между машинами. Двадцать семь лет спустя, переполнение буфера остаётся источником проблем. Разработчики Windows изменили свой подход к безопасности после двух основанных на переполнении буфера эксплойтов в начале двухтысячных. А обнаруженное в мае сего года переполнение буфера в Linux драйвере (потенциально) подставляет под удар миллионы домашних и SMB маршрутизаторов.
По своей сути, переполнение буфера является невероятно простым багом, происходящим из распространённой практики. Компьютерные программы часто работают с блоками данных, читаемых с диска, из сети, или даже с клавиатуры. Для размещения этих данных, программы выделяют блоки памяти конечного размера — буферы. Переполнение буфера происходит, когда происходит запись или чтение объёма данных большего, чем вмещает буфер.
На поверхности, это выглядит как весьма глупая ошибка. В конце концов, программа знает размер буфера, а значит, должно быть несложно удостоверится, что программа никогда не попытается положить в буфер больше, чем известный размер. И вы были бы правы, рассуждая таким образом. Однако переполнения буфера продолжают происходить, а результаты часто представляют собой катастрофу для безопасности.
Чтобы понять, почему происходит переполнение буфера — и почему результаты столь плачевны — нам нужно рассмотреть то, как программы используют память, и как программисты пишут код.
(Примечание автора: мы рассмотрим, в первую очередь, переполнение стекового буфера (stack buffer overflow). Это не единственный вид переполнения, но оно является классическим и наиболее изученным видом)
Стекируем
Переполнение буфера создаёт проблемы только в нативном коде — т.е. в таких программах, которые используют набор инструкций процессора напрямую, без посредников вроде Java или Python. Переполнения связаны с тем как процессор и программы в нативном коде управляют памятью. Различные операционные системы имеют свои особенности, но все современные распространённые платформы следуют общим правилам. Чтобы понять, как работают атаки, и какие бывают способы противодействия, сначала немного рассмотрим использование памяти.
Важнейшей концепцией является адрес в памяти. Каждый отдельный байт памяти имеет соответствующий числовой адрес. Когда процессор читает или записывает данные в основную память (ОЗУ, RAM), он использует адрес памяти того места, откуда происходит считывание или куда производится запись. Системная память используется не только для данных; она также используется для размещения исполняемого кода, из которого состоит программа. Это означает, что каждая из функций запущенной программы также имеет адрес.
Изначально, процессоры и операционные системы использовали адреса физической памяти: каждый адрес памяти напрямую соотносился с адресом конкретного куска RAM. Хотя, некоторые части современных операционных систем всё ещё используют физические адреса, все современные операционные системы используют схему, именуемую виртуальной памятью.
При использовании виртуальной памяти, прямое соответствие между адресом памяти и физическим участком RAM отсутствует. Вместо этого, программы и процессор оперируют в виртуальном пространстве адресов. Операционная система и процессор совместно поддерживают соответствие (mapping) между адресами виртуальной и физической памяти.
Такая виртуализация позволяет использовать несколько важных функций. Первая и важнейшая — это защищённая память. 64 — 1). Таким образом, у каждого процесса есть свой собственный адрес 0, за ним свой адрес 1, свой адрес 2 и так далее.
(Примечание автора: Далее в статье я буду говорить о 32-битный системах, если не указано иное. В данном аспекте разница между 32-битными и 64-битными несущественна; для ясности я буду придерживаться единой битности)
Поскольку каждый процесс получает свой собственный набор адресов, эта схема является простым способом предотвратить повреждение памяти одного процесса другим: все адреса к которым процесс может обращаться принадлежат только ему. Это гораздо проще и для самого процесса; адреса физической памяти, хотя они в широком смысле работают также (это просто номера, начинающиеся с нуля), имеют особенности, которые делают их несколько неудобными в использовании. Например, они обычно не-непрерывные; например, адрес 0x1ff8’0000 используется для памяти режима системного управления процессора — небольшой кусок памяти, недоступный обычным программам. Память PCIe-карт также находится в этом пространстве. С адресами виртуальной памяти таких неудобств нет.
Что же находится в адресном пространстве процесса? Говоря в общем, существуют четыре распространённых объекта, три из которых представляют для нас интерес. Неинтересный для нас блок, в большинстве операционных систем, — «ядро операционной системы». В интересах производительности, адресное пространство обычно разделяют на две половины, нижняя из которых используется программой, а верхняя занимается адресным пространством ядра. Половина, отданная ядру, недоступна из половины занятой программой, однако само ядро может читать память программы. Это является одним из способов передачи данных в функции ядра.
В первую очередь разберёмся с исполняемой частью и библиотеками, составляющими программу. Главный исполняемый файл (main executable) и все его библиотеки загружаются в адресное пространство процесса, и все составляющие их функции, таким образом, имеют адрес в памяти.
Вторая часть используемой программой памяти используется для хранения обрабатываемых данных и обычно называется кучей (heap). Эта область, например, используется для хранения редактируемого документа, или просматриваемой веб-страницы (со всеми её объектами JavaScrit, CSS и т.п.), или карты игры, в которую играют.
Третья и важнейшая часть — стек вызовов, обычно называемый просто стеком. Это самый сложный аспект. Каждый поток в процессе имеет свой стек. Это область памяти, используемая для одновременного отслеживания как текущей функции исполняемой в потоке, так и всех предшествующих функций — тех, что были вызваны, чтобы попасть в текущую функцию. Например, если функция a вызывает функцию b, а функция b вызывает функцию c, то стек будет содержать информацию об a, b и c, в таком порядке.
Стек вызовов является специализированной версией структуры данных, называемой «стеком». Стеки являются структурами переменной длины, предназначенными для хранения объектов. Новые объекты могут быть добавлены (pushed) в конец стека (обычно называемого «вершиной» стека) и объекты могут быть сняты (popped) со стека. Только вершина стека подлежит изменению с использованием push и pop, таким образом, стек устанавливает строгий порядок сортировки: объект, который последним положили в стек, будет тем, который будет снят с него следующим.
Важнейшим объектом, хранимым в стеке вызовов, является адрес возврата (return address). В большинстве случаев, когда программа вызывает функцию, эта функция выполняет то, что должна (включая вызов других функций), а затем возвращает управление в функцию, которая её вызвала. Для возврата к вызывающей функции необходимо сохранить запись о ней: исполнение должно продолжиться с инструкции следующей после инструкции вызова. Адрес этой инструкции называется адресом возврата. Стек используется для хранения этих адресов возврата: при каждом вызове функции, в стек помещается адрес возврата. При каждом возврате, адрес снимается со стека и процессор начинает выполнять инструкцию по этому адресу.
Стековая функциональность является настолько базовой и необходимой, что большинство, если не все процессоры имеют встроенную поддержку этих концепций. Возьмём за пример процессоры x86. Среди регистров (небольших участков памяти в процессоре, доступных инструкциям), определённых в спецификации x86, два наиболее важных — eip (указатель инструкции — instruction pointer), и esp (указатель стека — stack pointer).
ESP всегда содержит адрес вершины стека. Каждый раз когда что-то добавляется в стек, значение esp уменьшается. Каждый раз, когда что-то снимается со стека, значение esp увеличивается. Это означает, что стек растёт «вниз»; по мере добавления объектов в стек, адрес хранимый в esp становится всё меньше и меньше. Несмотря на это, область памяти, на которую указывает esp, называется «вершиной стека.
Здесь мы видим простую развёртку стека с 64-символьным буфером с именем name, за ним указатель вложенного кадра (frame pointer), потом адрес возврата. В регистре esp содержится адрес вершины, в ebp — адрес указателя кадра.
EIP содержит адрес текущей инструкции. Процессор поддерживает значение eip самостоятельно. Он читает поток инструкций из памяти и изменяет значение eip соответственно, так что он всегда содержит адрес инструкции. В рамках x86 существует инструкция для вызова функций, call, а также инструкция для возврата — ret.
CALL принимает один операнд, адрес вызываемой функции (хотя есть несколько способов передать его). Когда выполняется call, указатель стека esp уменьшается на 4 байта (32 бита), и адрес инструкции следующей за call — адрес возврата — помещается в область памяти, на которую теперь указывает esp. Другими словами, адрес возврата помещается в стек. Затем, значением eip устанавливается равным адресу, переданному в качестве операнда call, и выполнение продолжается с этой точки.
RET производит обратную операцию. Простой ret не принимает операндов. Процессор сначала считывает значение по адресу памяти, хранимому в esp, потом увеличивает esp на 4 байт — снимает адрес возврата со стека. Значение помещается в eip, и выполнение продолжается с этого адреса.
(Примечание переводчика: в этом месте в авторском тексте приводится видео с демонстрацией call и ret.)
Если бы стек вызовов хранил только набор адресов возврата, проблемы бы не было. Реальная проблема приходит со всем остальным, что кладут в стек. Так выходит, что стек — это быстрое и эффективное место хранения данных. Хранение данных в куче относительно сложно: программа должна отслеживать доступное в куче место, сколько занимает каждый из объектов и прочее. При этом работа со стеком проста: чтобы разместить немного данных, достаточно просто уменьшить значение указателя. А чтобы почистить за собой, достаточно увеличить значение указателя.
Это удобство делает стек логичным местом для размещения переменных, используемых функцией. Функции нужно 256 байт буфера, чтобы принять ввод пользователя? Легко, просто отнимите 256 от указателя стека — и буфер готов. В конце функции, просто прибавьте 256 к указателю, и буфер отброшен.
Однако, у такого подхода существуют ограничения. Стек не подходит для хранения очень больших объектов: общий объём доступной памяти обычно фиксирован при создании потока и, часто, составляет примерно 1МБ в объёме. Поэтому большие объекты должны быть помещены в кучу. Стек также не применим для объектов, которые должны существовать дольше, чем выполняется одна вызванная функция. Поскольку все размещения в стеке удаляются при выходе из функции, время жизни любого из объектов в стеке не превышает времени выполнения соответствующей функции. На объекты в куче это ограничение не распространяется, они могут существовать „вечно“.
Когда мы используем программу корректно, ввод с клавиатуры сохраняется в буфере name, закрываемым нулевым (null, zero) байтом. Указатель кадра и адрес возврата не изменяются.
Стековое хранилище используется не только для явно определяемых программистом переменных; стек также используется для хранения любых значений, нужных программе. Особенно остро это проявляется в x86. Процессоры на базе x86 не отличаются большим числом регистров (всего существует 8 целочисленных регистров, и некоторые из них, как уже упомянутые eip и esp, уже заняты), поэтому функции редко имеют возможность хранить все необходимые им значения в регистрах. Чтобы освободить место в регистрах, и при этом сохранить значение для последующего использования, компилятор поместит значение регистра в стек. Значение позднее может быть снято с регистра и помещено обратно в регистр. В жаргоне компиляторов, процесс сохранения регистров с возможностью последующего использования называется spilling.
Наконец, стек часто используют для передачи аргументов функциям. Вызывающая функция помещает каждый из аргументов по очереди в стек; вызываемая функция может снять их из стека. Это не единственный способ передачи аргументов — можно использовать и регистры, например, — но это один из наиболее гибких.
Набор объектов хранимых функцией в стеке — её собственные переменные, сохранённые регистры, любые аргументы, подготавливаемые для передачи в другие функции — называются „вложенным кадром“. Поскольку данные во вложенном кадре активно используются, полезно иметь способ простой адресации к нему.
Это возможно реализовать, используя указатель стека, но это несколько неудобно: указатель стека всегда указывает на вершину, и его значение меняется по мере помещения и снятия объектов. Например, переменная может сначала быть расположена на позиции esp+4. После того, как ещё два значения положили в стек, и переменная стала располагаться по адресу esp+12. Если снять со стека одно из значений, переменная окажется на esp+8.
Описанное не является неподъёмной задачей, и компиляторы способны с ней справиться. Однако это делает использование указателя стека для доступа к чему-либо кроме вершины „стрёмным“, особенно при написании на ассемблере вручную.
Для упрощения задачи, обычным делом является ведение второго указателя, который хранит адрес „дна“ (т.е. начала) каждого кадра — значение, известное как указатель вложенного кадра (frame pointer). И на x86 даже есть регистр, который для этого обычно используют, ebp. Поскольку его значение неизменно в пределах функции, появляется способ однозначно адресовать переменные функции: значение, лежащее по адресу ebp-4, будет оставаться доступно по ebp-4 всё время жизни функции. И это полезно не только для людей — дебаггерам проще разобраться, что происходит.
Скриншот из Visual Studio демонстрирует всё это в действии на примере простой программы для x86. На процессорах x86, регистр esp содержит адрес вершины стека, в данном случае 0x0019fee0 (выделено синим). (Примечание автора: на платформе x86, стек растёт вниз, в направлении адреса памяти 0, однако эта точка всё равно сохраняет название „вершина стека“). Показанная функция хранит в стеке только переменную name, выделенную розовым цветом. Это фиксированный буфер длиной 64 байта. Поскольку это единственная переменная, её адрес тоже 0x0019fee0, такой же, как у вершины стека.
В x86 также есть регистр ebp, выделенный красным, который (обычно) выделен для хранения указателя кадра. Указатель кадра размещается сразу за переменными стека. Сразу за указателем кадра лежит адрес возврата, выделенный зелёным. Адрес возврата ссылается на фрагмент кода по адресу 0x00401048. Эта инструкция следует сразу за вызовом (call), демонстрируя то, как адрес возврата используется для продолжения исполнения там, где программа покинула вызывающую функцию.
NAME в приведённой иллюстрации относится как раз к тому роду буферов, которые регулярно переполняются. Его размер зафиксирован и составляет 64 байта. В данном случае, он заполнен набором чисел и завершается нулём. Из иллюстрации видно, что если в буфер name будет записано более 64 байт, то другие значения в стеке будут повреждены. Если записать на четыре байта больше, указатель кадра будет уничтожен. Если записать на восемь байт больше, то и указатель кадра, и адрес возврата будут перезаписаны.
Очевидно, что это ведёт к повреждению данных программы, но проблема с переполнением буфера куда серьёзнее: они ведут к выполнению [произвольного] кода. Это происходит потому, что переполненный буфер не просто перезапишет данные. Также могут оказаться перезаписаны более важные вещи, хранимые в стеке — адреса возврата. Адрес возврата контролирует то, какие инструкции процессор будет выполнять, когда закончит с текущей функцией; предполагается, что это будет какой-то адрес внутри вызывающей функции, но если это значение будет переписано переполнением буфера, оно может указывать куда угодно. Если атакующие могут контролировать переполнение буфера, то они могут контролировать и адрес возврата. Если они контролируют адрес возврата, они могут указать процессору, что делать дальше.
У процессора, скорее всего, нет красивой удобной функции „скомпрометировать машину“, которую бы запустил атакующий, но это не слишком важно. Тот же буфер, который используется для изменения адреса возврата, можно использовать для хранения небольшого куска исполнимого кода (shellcode, шеллкод), который, в свою очередь, скачает вредоносный исполнимый файл, или откроет сетевое соединение, или исполнит любые другие пожелания атакующего.
Традиционно, сделать это было тривиально просто, по причине, которая у многих вызывает удивление: обычно, каждая программа будет использовать одни и те же адреса в памяти при каждом запуске, даже если вы перезагружали машину. Это означает, что позиция буфера в стеке всякий раз будет одинакова, а значит и значение, используемое для искажения адреса возврата, каждый раз будет одинаково. Атакующему достаточно лишь выяснить этот адрес однажды, и атака сработает на любом компьютере, исполняющем уязвимый код.
Инструментарий атакующего
В идеальном мире — с точки зрения атакующего — переписанный адрес возврата может быть просто адресом буфера. И это вполне возможно, когда программа читает данные из файла или из сети.
В других случаях, атакующий должен идти на хитрости. В функциях, обрабатывающих человеко-читаемый текст, байт с нулевым значением (null) часто имеет специальное значение; такой байт обозначает конец строки, и функции используемые для манипуляции строками — копирование, сравнение, совмещение — останавливаются. когда встречают этот символ. Это означает, что если шеллкод содержит ноль, эти процедуры его сломают.
(Примечание переводчика: в этом месте в авторском тексте приводится видео с демонстрацией переполнения. В нём, в буфер помещают шеллкод и переписывают адрес возврата. Шеллкод запускает стандартный калькулятор Windows.)
Чтобы это обойти, атакующий может использовать различные приёмы. Небольшие фрагменты кода для конвертирования шеллкода с нулями в эквивалентную последовательность, избегающую проблемный байт. Так возможно пролезть даже через очень строгие ограничения; например, уязвимая функция принимает на вход только данные, которые можно набрать со стандартной клавиатуры.
Собственно адрес стека часто содержит нули, и здесь есть сходная проблема: это означает, что адрес возврата нельзя записать адрес из стекового буфера. Иногда это не страшно, потому что некоторые из функций, используемых для заполнения (и, потенциально, переполнения) буферов сами пишут нули. Проявляя некоторую осторожность, их можно использовать, чтобы поместить нулевой байт точно в нужное место, устанавливая в адресе возврата адрес стека.
Но даже когда это невозможно, ситуация обходится окольными путями (indirection). Собственно программа со всеми своими библиотеками держит в памяти огромное количество исполнимого кода. Большая часть этого кода будет иметь „безопасный“ адрес, т.е. не будет иметь нулей в адресе.
Тогда, атакующему нужно найти подходящий адрес, содержащий инструкцию вроде call esp (x86), которая использует значение указателя стека в качестве адреса функции и начинает её исполнение, чем идеально подходит для шеллкода спрятанного в стековом буфере. Атакующий использует адрес инструкции call esp для записи в качестве адреса возврата; процессор сделает лишний прыжок через этот адрес, но всё равно попадёт на шеллкод. Этот приём с прыжком через другой адрес называется „трамплином“.
Для эксплуатации переполнения, вместо того чтобы просто забить всё некими символами, атакующий пишет в буфер шеллкод: короткий участок исполнимого кода, который выполнит некое выбранное атакующим действие. Адрес возврата переписывается соответствующим адресом из буфера, заставляя процессор исполнять шеллкод при попытке возврата из процедуры.
Это работает потому, повторюсь, что программа и её библиотеки при каждом запуске размещаются в одни и те же области памяти — даже между перезагрузками и даже на разных машинах. Одним из интересных моментов в этом деле является то, что библиотеке, от которой выполняется трамплин, самой даже не нужно использовать оператор call esp. Достаточно, чтобы в ней были два подходящих байта (в данном случае, со значениями 0xffи 0xd4) идущие друг-за-другом. Они могут быть частью какой-то иной функции, или даже просто числом; x86 не привередлива к таким вещам. Инструкции x86 могут быть очень длинными (до 15 байт!) и могут располагаться по любому адресу. Если процессор начнёт читать инструкцию с середины — со второго байта четырёхбайтной инструкции, к примеру — результат будет интерпретирован как совсем иная, но всё же валидная, инструкция. Это обстоятельство делает нахождение полезных трамплинов достаточно простым.
Иногда, однако, атака не может установить адрес возврата в точности куда требуется. Несмотря на то, что расположение объектов в памяти крайне схоже, оно может слегка отличаться от машины к машине или от запуска к запуску. Например, точное расположение подверженного атаке буфера может варьироваться вверх и вниз на несколько байт, в зависимости от имени системы или её IP-адреса, или потому, что минорное обновление программы внесло незначительное изменение. Чтобы справится с этим, полезно иметь возможность указать адрес возврата который примерно верен, но высокая точность не нужна.
Это легко делается с использованием приёма, называемого „посадочной полосой“ (NOP sled, букв. „сани из NOPов“ (спасибо Halt за корректный русскоязычный термин — прим.пер.)). Вместо того, чтобы писать шеллкод сразу в буфер, атакующий пишет большое число инструкций NOP (означающих „no-op“, т.е. отсутствие операции — говорит процессору ничего не делать), иногда сотни, перед настоящим шеллкодом. Для запуска шеллкода, атакующему нужно установить адрес возврата на позицию где-то посреди этих NOPов. И если мы попали в область NOPов, процессор быстро обработает их и приступит к настоящему шеллкоду.
Иногда сложно переписать адрес возврата адресом из буфера. В качестве решения, мы можем переписать адрес возврата адресом части исполнимого кода в программе-жертве (или её библиотеках). И этот фрагмент уже передаст управление в буфер.
Во всём нужно винить C
Главный баг, который позволяет всё это сделать — записать в буфер больше, чем доступно места — выглядит как что-то, что легко избежать. Это преувеличение (хоть и небольшое) возлагать всю ответственность на язык программирования C, или его более или менее совместимых отпрысков, конкретно C++ и Objective C. Язык C стар, широко используем, и необходим для наших операционных систем и программ. Его дизайн отвратителен, и хотя всех этих багов можно избежать, C делает всё, чтобы подловить неосторожных.
В качестве примера враждебности C к безопасной разработке, взглянем на функцию gets(). Эта функция принимает один параметр — буфер — и считывает строку данных со стандартного ввода (что, обычно, означает „клавиатуру“), и помещает её в буфер. Наблюдательный читатель заметит, что функция gets() не включает параметр размера буфера, и как забавный факт дизайна C, отсутствует способ для функции gets() определить размер буфера самостоятельно. Это потому, что для gets() это просто не важно: функция будет читать из стандартного ввода, пока человек за клавиатурой не нажмёт клавишу Ввод; потом функция попытается запихнуть всё это в буфер, даже если этот человек ввёл много больше, чем помещается в буфер.
Это функция, которую в буквальном смысле нельзя использовать безопасно. Поскольку нет способа ограничить количество набираемого с клавиатуры текста, нет и способа предотвратить переполнение буфера функцией gets(). Создатели стандарта языка C быстро поняли проблему; версия спецификации C от 1999 года выводила gets() из обращения, а обновление от 2011 года полностью убирает её. Но её существование — и периодическое использование — показывают, какого рода ловушки готовит C своим пользователям.
Червь Морриса, первый само-распространяющийся зловред который расползся по раннему Интернету за пару дней в 1988, эксплуатировал эту функцию. Программа fingerd в BSD 4.3 слушает сетевой порт 79, порт finger. Finger является древней программой для Unix и соответствующим сетевым протоколом, используемым для выяснения того, кто из пользователей вошёл в удалённую систему. Есть два варианта использования: удалённую систему можно опросить и узнать всех пользователей, осуществивших вход, или можно сделать запрос о конкретном юзернейме, и программа вернёт некоторую информацию о пользователе.
К сожалению, gets() довольно глупая функция. Достаточно зажать клавишу А на клавиатуре, и она не остановится после заполнения буфера name. Она продолжит писать данные в память, перезаписывая указатель кадра, адрес возврата и всё остальное, до чего сможет дотянуться.
Каждый раз при сетевом подключении к демону finger, он начинал чтение с сети — используя gets() — в стековый буфер длиной 512 байт. При нормальной работе, fingerd затем запускал программу finger, передавая ей имя пользователя (если оно было). Программа finger выполняла реальную работу по перечислению пользователей или предоставлению информации о конкретном пользователе. Fingerd просто отвечала за сетевое соединение и запуск finger.
Учитывая, что единственный „реальный“ параметр это необязательное имя пользователя, 512 байт является достаточно большим буфером. Скорее всего ни у кого нет имени пользователя и близко такой длины. Однако, нигде в системе это ограничение не было жёстким по причине использования ужасной функции gets(). Пошлите больше 512 байт по сети и fingerd переполнит буфер. И именно это сделал Роберт Моррис (Robert Morris): его эксплоит отправлял в fingerd 537 байт (536 байт данных и перевод строки, заставлявший gets() прекратить чтение), переполняя буфер и переписывая адрес возврата. Адрес возврата был установлен просто в области стекового буфера.
Исполнимая нагрузка червя Моррис была простой. Она начиналась с 400 инструкций NOP, на случай если раскладка стека будет слегка отличаться, затем короткий участок кода. Этот код вызывал шелл, /bin/sh. Это типичный вариант атакующей нагрузки; программа fingerd запускалась под рутом, поэтому, когда при атаке она запускала шелл, шелл тоже запускался под рутом. Fingerd была подключена к сети, принимая „клавиатурный ввод“ и аналогично отправляя вывод обратно в сеть. И то и другое наследовал шелл вызванный эксплойтом, и это означало, что рутовый шелл теперь был доступен атакующему удалённо.
Несмотря на то, что использования gets() легко избежать — даже во время распространения червя Морриса была доступна версия fingerd не использовавшая gets() — прочие компоненты C сложнее игнорировать, и они не менее подвержены ошибкам. Типичной причиной проблем является обработка строк в C. Поведение, описанное ранее — останов на нулевых байтах — восходит к поведению строк в C. В языке C, строка представляет собой последовательность символов, завершаемую нулевым байтом. В C существует набор функций для работы со строками. Возможно, лучшим примером являются strcpy(), копирующая строку из одного места в другое, и strcat(), вставляющая исходную строку следом за точкой назначения. Ни одна из этих функций не имеет параметра размера буфера назначения. Обе с радостью будут бесконечно читать из источника, пока не встретят NULL, заполняя буфер назначения и беззаботно переполняя его.
Даже если строковая функция в C имеет параметр размера буфера, она реализует это способом, ведущим к ошибкам и переполнениям. В языке C есть пара функций родственных strcat() и strcpy(), называемых strncat() и strncpy(). Буква n в именах этих функций означает что они, в некотором роде, принимают размер в качестве параметра. Однако n, хотя многие наивные программисты думают иначе, не является размером буфера в который происходит запись — это число символов для считывания из источника. Если в источнике символы закончились (т.е. достигнут нулевой байт), то strncpy() и strncat() заполнят остаток нулями. Ничто в этих функциях не проверяет истинный размер назначения.
В отличии от gets(), эти функции возможно использовать безопасным образом, только это не просто. В языках C++ и Objective-C есть лучшие альтернативы этим функциям C, что делает работу со строками проще и безопаснее, однако функции C также поддерживаются в целях обратной совместимости.
Более того, они сохраняют фундаментальный недостаток языка C: буферы не знают своего размера, и язык никогда не проверяет выполняемые над буферами чтения и записи, допуская переполнение. Именно такое поведение привело к недавнему багу Heartbleed в OpenSSL. То не было переполнение, а перечтение, когда код на C в составе OpenSSL пытался прочитать из буфера больше чем тот содержал, сливая информацию наружу.
Латание дыр
Конечно, человечество разработало множество языков в которых осуществляется проверка чтения и записи в буферы, что защищает от переполнения. Компилируемые языки, такие как поддерживаемый Mozilla язык Rust, защищённые среды исполнения вроде Java и .NET, и практически все скриптовые языки вроде Python, JavaScript, Lua и Perl имеют иммунитет к этой проблеме (хотя в .NET разработчики могут явным образом отключить защиту и подвергнуть себя подобному багу, но это личный выбор).
Тот факт, что переполнение буфера продолжает оставаться частью ландшафта безопасности, говорит о популярности C. Одой из причин этого, конечно, является большое количество унаследованного кода. В мире существует огромное количество кода на C, включая ядра всех основных операционных систем и популярных библиотек, таких как OpenSSL. Даже если разработчики хотят использовать безопасный язык, вроде C#, у них могут оставаться зависимости от сторонних библиотек, написанных на C.
Производительность является другой причиной продолжающегося использования C, хотя смысл такого подхода не всегда понятен. Верно, что компилируемые C и С++ обычно выдают быстрый исполняемый код, и в некоторых случаях это действительно очень важно. Но у многих из нас процессоры большую часть времени простаивают; если бы мы могли пожертвовать, скажем, десятью процентами производительности наших браузеров, но при этом получить железную гарантию невозможности переполнения буфера — и других типичных дыр, мы может быть бы решили, что это не плохой размен. Только никто не торопится создать такой браузер.
Несмотря ни на что, C сотоварищи никуда не уходит; как и переполнение буфера.
Предпринимаются некоторые шаги по предупреждению этого рода ошибок. В ходе разработки, можно использовать специальные средства анализа исходного кода и запущенных программ, стараясь обнаружить опасные конструкции или ошибки переполнения до того, как эти баги пролезут в релиз. Новые средства, такие как AddressSanitizer и более старые, как Valgrind дают такие возможности.
Однако, эти оба этих инструмента требуют активного вмешательства разработчика, что означает, что не все программы их используют. Системные средства защиты, ставящие целью сделать переполнения буфера менее опасными, когда они случаются, могут защитить много больше различного программного обеспечения. Понимая это, разработчики операционных систем и компиляторов внедрили ряд механизмов, усложняющих эксплуатацию этих уязвимостей.
Некоторые из этих систем нацелены на усложнение конкретных атак. Один из наборов патчей для Linux делает так, что все системные библиотеки загружаются в нижние адреса таким образом, чтобы содержать, по крайней мере, один нулевой байт в своём адресе; это существенно усложняет их использование в переполнениях эксплуатирующих обработку строк в C.
Другие средства защиты действуют более обще. Во многих компиляторах имеется какой-либо род защиты стека. Определяемое на этапе исполнения значение, называемое „канарейкой“ (canary) пишется в конец стека рядом с адресом возврата. В конце каждой функции, это значение проверяется перед выполнением инструкции возврата. Если значение канарейки изменилось (по причине перезаписи в ходе переполнения), программа немедленно рухнет вместо продолжения.
Возможно, важнейшим из средств защиты является механизм известный под именами W^X (»write exclusive-or execute»), DEP («data execution prevention»), NX («No Xecute»), XD («eXecute Disable»), EVP («Enhanced Virus Protection,» специфичный для AMD термин), XN («eXecute Never»), и, вероятно, другими. Здесь принцип прост. Эти системы стараются разделить память на записываемую (подходящую для буферов) и исполнимую (подходящую для библиотек и программного кода), но не одновременно ту и другую. Таким образом, даже если атакующий может переполнить буфер и контролировать адрес возврата, процессор не будет выполнять шеллкод.
Как бы вы его не назвали, это важный механизм ещё и потому, что он не требует вложений. Этот подход использует защитные меры встроенные в процессор, поскольку это часть механизма аппаратной поддержки виртуальной памяти.
Как уже говорилось ранее, в режиме виртуальной памяти каждый процесс получает свой набор частных адресов памяти. Операционная система и процессор совместно поддерживают соотношение виртуальных адресов к чему-то ещё; иногда, виртуальный адрес отображается на физическую память, иногда в часть файла на диске, а иногда в никуда, просто потому что он не распределён. Это соотнесение гранулярно и обычно происходит частями размером в 4096 байт, именуемыми страницами.
Структуры данных, используемые для отображения, включают не только местоположение (физическая память, диск, нигде) каждой страницы; они также обычно содержат три бита, определяющие защиту страницы: доступна ли страница для чтения, записи и исполнения. С такой защитой, области памяти процесса используемые под данные, такие как стек, могут быть помечены на чтение и запись, но не быть исполнимыми.
Одним из интересных моментов NX является то, что его можно применить к существующим программам «задним числом», просто путём обновления операционной системы до той, что поддерживает защиту. Иногда программы налетают на проблемы. JIT (Just-in-time)-компиляторы, используемые в Java и .NET, генерируют исполнимый код в памяти на этапе исполнения, и поэтому требуют память, которую можно и писать и исполнять (хотя, одновременность этих свойств не требуется). Когда ещё не было NX, вы могли исполнять код из любой памяти, которую могли читать, поэтому в таких JIT-компиляторах не было проблемы с особыми буферами чтения-записи. С появлением NX, от них требуется удостовериться, что защита памяти изменена с чтение-запись на чтение-исполнение.
Потребность в чём-то вроде NX была ясна, особенно для Microsoft. В начале 2000-х, пара червей показала, что у компании были серьёзные проблемы с безопасностью кода: Code Red, инфицировавший не менее 359000 систем под управлением Windows 2000 с сервисом Microsoft IIS Web server в июле 2001, и SQL Slammer, инфицировавший более 75000 систем с Microsoft SQL Server в январе 2003. Эти случаи хорошо ударили по репутации.
Оба червя эксплуатировали стековое переполнение буфера, и, удивительно, что хотя они появились тринадцатью и пятнадцатью годами позже червя Морриса соответственно, метод эксплуатации был почти идентичен. Нагрузка эксплойта помещалась в буфер на основе стека и переписывала адрес возврата (одним небольшим отличием являлось то, что оба червя использовали метод трамплина. Вместо прямого адреса стека, в адрес возврата прописывался адрес инструкции, которая возвращала управление в стек. ).
Естественно, эти черви были более продвинуты и в других областях. Нагрузка Code Red не просто самовоспроизводилась; она производила дефейс веб-страниц и пыталась выполнять DoS-атаки. SQL Slammer нёс в себе всё необходимое для поиска новых целей для заражения и распространения по сети — всего в нескольких сотнях байт, при этом не оставляя следов на инфицированных машинах; перезагрузите машину — и его нет. Оба червя также работали в Интернете, который был многократно больше того, в котором распространился червь Морриса, и потому число заражений было сильно выше.
Однако основная проблема — легко эксплуатируемое переполнение стекового буфера — осталась прежней. Эти черви оказались в заголовках новостей и заставили многих сомневаться в возможности использовать Windows любого рода в качестве сервера, смотрящего в Интернет. Ответом Microsoft было начать всерьёз задумываться о безопасности. Windows XP Service Pack 2 была первым продуктом с установкой на безопасность. Было сделано несколько программных изменений, включая добавление программного межсетевого экрана, модификация Internet Explorer, препятствующая тихой установке тулбаров и плагинов, а также — поддержка NX.
Аппаратное обеспечение с поддержкой NX стало входить в быт где-то с 2004 года, когда Intel представила Prescott Pentium 4, поддержка со стороны операционных систем стала обыденностью со времён Windows XP SP2. В Windows 8 они решили ещё больше форсировать этот момент, отказавшись от поддержки процессоров, не умеющих NX.
Что было после NX
Несмотря на распространение поддержки NX, переполнение буфера остаётся актуальной проблемой информационной безопасности. Причиной тому является разработка ряда способов обхода NX.
Первый из них был похож на вышеописанный трамплин, передающий контроль шеллкоду в стековом буфере через инструкцию расположенную в другой библиотеке или исполнимом файле. Вместо того чтобы искать фрагмент исполнимого кода, который бы передал управление напрямую в стек, атакующий находит фрагмент, который сам делает что-то полезное.
Возможно, лучшим кандидатом на эту роль является Unix-функция system(). Она принимает один параметр: адрес строки, представляющей собой команду для исполнения — и обычно этот параметр передаётся через стек. Атакующий может создать нужную команду и поместить её в переполняемый буфер, а поскольку (традиционно) расположение объектов в памяти неизменно, адрес этой строки будет известен и может быть помещён на стек в ходе атаки. Переписанный адрес возврата в этом случае не указывает на адрес в буфере; он указывает на функцию system(). Когда функция подверженная переполнению завершает работу, вместо возврата в вызывающую функцию она запустит system(), что приведёт к исполнению заданной атакующим команды.
Вот так можно обойти NX. Функция system(), будучи частью системной библиотеки, уже исполнима. Эксплойту не требуется исполнять код из стека; достаточно прочитать команду с него. Этот приём получил название «return-to-libc» (возврат на libc, библиотеки Unix, содержащей множество ключевых функций, включая system(), и обычно загружаемой в каждый Unix-процесс, что делает её подходящей целью для такого использования) и был изобретён в 1997 году русским экспертом по информационной безопасности Solar Designer.
Хотя этот приём и полезен, у него есть ограничения. Часто функции принимают аргумент не со стека, а через регистры. Удобно передавать команды для исполнения, но они часто содержат эти дурацкие нули, что немало мешает. Кроме того, составить последовательность из различных вызовов таким способом весьма непросто. Это возможно — прописать несколько адресов возврата вместо одного — но нет способа изменить порядок следования аргументов, используя возвращаемые значения или что-либо ещё.
Вместо заполнения буфера шелкодом, мы заполняем его последовательностью адресов возврата и данными. Эти адреса возврата передают управление существующим фрагментам исполнимого кода в программе-жертве и её библиотеках. Каждый фрагмент кода выполняет операцию и выполняет возврат, передавая управление по следующему адресу возврата.
За несколько лет, return-to-libc был обобщён для обхода этих ограничений. В конце 2001 было задокументировано несколько вариантов расширения этого способа: возможность нескольких вызовов и решение проблемы нулевых байтов. Более сложный способ, решавший большую часть этих проблем, был формально описан в 2007 году: return-oriented-programming (ROP, возвратно-ориентированное программирование).
Здесь используется тот же принцип что и в return-to-libc и трамплине, но более обобщённый. Там где трамплин использует единственный фрагмент кода для передачи исполнения шеллкоду в буфере, ROP использует много фрагментов кода, называемых «гаджетами» в оригинальной публикации. Каждый гаджет следует определённому шаблону: он выполняет некую операцию (запись значения в регистр, запись в память, сложение регистров, и т.п.), за которой следует команда возврата. То самое свойство, что делает x86 пригодным для трамплина работает и здесь; системные библиотеки, загруженные в память процессом, содержат сотни последовательностей которые можно интерпретировать как «действие и возврат», а значит, могут быть использованы для ROP-атак.
Для объединения гаджетов в одно целое используется длинная последовательность адресов возврата (а также любых полезных и необходимых данных) записанных в стек в ходе переполнения буфера. Инструкции возврата прыгают с гаджета на гаджет, в то время как процессор редко (или никогда) вызывает функции, а только возвращается из них. Интересно то, что по крайней мере на x86, число и разнообразие полезных гаджетов таково, что атакующий в прямом смысле может делать всё что угодно; это подмножество x86, используемое особым образом, зачастую является Тьюринг-полным (хотя полный спектр возможностей будет зависеть от загружаемых программой библиотек, и следственно перечнем доступных гаджетов).
Как и в случае с return-to-libc, весь действительно исполнимый код берётся из системных библиотек, и как следствие защита вроде NX бесполезна. Большая гибкость этого подхода означает, что эксплойты могут делать то, что сложно организовать последовательностью return-to-libc, например, вызывая функции принимающие аргументы через регистры, использовать возвращаемые значения одних функций в других и прочее.
Нагрузка в ROP-атаках бывает разной. Иногда это простой код для получения шелла (доступа к командному интерпретатору). Другим распространённым вариантом является использование ROP для вызова системной функции для изменения NX-параметров страницы памяти, меняя их с записываемых на исполнимые. Сделав это, атакующий может использовать обычную, не-ROP нагрузку.
Рандомизация
Эта слабость NX давно известна, и эксплойты такого типа шаблоны: атакующий заранее знает адрес стека и системных библиотек в памяти. Всё зиждется на этом знании, а потому очевидным решением является лишить атакующего этого знания. Именно этим занимается ASLR (Address Space Layout Randomization, Рандомизация развёртки адресного пространства): он делает случайной позицию стека и расположение в памяти библиотек и исполнимого кода. Обычно они меняются при каждом запуске программы, перезагрузке или некоторой их комбинации.
Данное обстоятельство значительным образом осложняет эксплуатацию, поскольку, совершенно неожиданно, атакующий не знает где лежат нужные для ROP фрагменты инструкций, или хотябы где находится переполняемый стек.
ASLR во многом сопутствует NX, закрывая такие крупные дыры как возврат к libc или ROP. К несчастью, он несколько менее прозрачен, чем NX. Не считая JIT-компиляторов и ряда других специфичных случаев, NX может быть безопасно внедрён в существующие программы. ASLR более проблематичен: с ним программы и библиотеки не могут полагаться в своей работе на значение адреса, в который они загружены.
В Windows, например, это не должно быть большой проблемой для DLL. В Windows, DLL всегда поддерживали загрузку в разные адреса, а вот для EXE это может быть проблемой. До ASLR, EXE всегда загружались в адрес 0x0040000 и могли полагаться на этот факт. С внедрением ASLR это уже не так. Чтобы предотвратить возможные проблемы, Windows по умолчанию требует от программ явного указания поддержки ASLR. Люди, думающие о безопасности, могут, однако, изменить это поведение по умолчанию, заставив Windows включить ASLR для всех программ и библиотек. Это почти никогда не вызывает проблем.
Ситуация вероятно хуже в Linux на x86, поскольку подход к реализации ASLR на этой платформе даёт потерю производительности до 26 процентов. Более того, этот подход требует компиляции программ и библиотек с поддержкой ASLR. Нет способа администратору сделать ASLR принудительным, как в Windowsю (на x64 потеря производительности пусть и не уходит совсем, но значительно снижается)
Когда ASLR активен, он даёт сильную защиту от простого взлома. Однако, он не совершенен. Например, одним из ограничений является степень случайности, которую можно получить, что особенно заметно на 32-битных системах. Хотя в адресном пространстве 4 миллиарда различных адресов, не все они доступны для загрузки библиотек или размещения стека.
Для этого существует множество ограничений. Некоторые из них состоят в широте целей. В общем случае, операционная система предпочитает загружать библиотеки близко друг-к-другу на одном из концов адресного пространства процесса, чтобы как можно больше непрерывного места было доступно приложению. Вам не хочется получить по одной библиотеке через каждые 256МБ памяти, поскольку тогда возможное наибольшее унитарное выделение памяти будет меньше 256МБ, что ограничивает возможность приложения работать с данными большого объёма.
Исполнимые файлы и библиотеки обычно должны быть загружены так чтобы начинаться, по крайней мере, на границе страницы. Обычно, это означает, что они должны быть загружены в адрес, делимый на 4096. Различные платформы могут и иметь подобные ограничения для стека; Linux, например, начианет стек на адресе делимом на 16. Системы с ограничением по памяти иногда вынуждены ещё более ограничить случайность, чтобы иметь возможность всё разместить.
Результаты бывают различными, но иногда атакующий могут угадать нужный адрес, с высокой вероятностью попадания. Даже невысокого шанса — скажем, один из 256 — может быть достаточно в некоторых ситуациях. Когда атакуешь веб-сервер, который автоматически перезапустит рухнувший процесс, не важно, что 255 из 256 атак приведут к краху процесса. Он будет перезапущен, и можно попробовать снова.
Но на 64-битных системах, адресное пространство так велико, что такой подход не поможет. У атакующего может быть только один шанс из миллиона или даже один из миллиарда, и это достаточно мало, чтобы это не было важно.
Угадывание и падение не слишком хорошая стратегия для атаки, скажем, браузеров; ни один пользователь не будет перезапускать браузер 256 раз кряду лишь бы дать атакующему шанс. В результате, эксплуатация такой уязвимости в системе с активными NX и ASLR не может быть произведена без посторонней помощи.
Такая помощь может быть нескольких видов. В браузере можно использовать JavaScript или Flash — и то и другое содержит JIT-компиляторы генерирующие исполнимый код — для заполнения памяти аккуратно сконструированным исполнимым кодом. Это создаёт что-то вроде большой посадочной полосы, приём под названием «heap spraying» («напыление кучи»). Другим подходом может быть нахождение вторичного бага, позволяющего раскрыть адреса библиотек или стека в памяти, давая атакующему достаточно информации для создания специфичного набора возвратных адресов для ROP.
Третий подход также был популярен в браузерах: использовать библиотеки, не умеющие ASLR. Старые версии, например, плагинов Adobe PDF или Microsoft Office не поддерживали ASLR, и Windows по умолчанию не форсирует ASLR. Если атакующий может вызвать загрузку такой библиотеки (например, загрузив PDF в скрытом фрейме браузера), то об ASLR можно уже не беспокоиться, а использовать эту библиотеку для целей ROP.
Война без конца
Между теми, кто эксплуатирует уязвимости и теми, кто защищает, идёт постоянная гонка вооружений. Мощные защитные системы, вроде ASLR и NX, поднимают планку, усложняя использование недостатков, и благодаря им мы оставили времена простого переполнения буфера позади, но умные атакующие могут найти комбинацию дыр и обойти эти защитные меры.
Эскалация продолжается. Набор Microsoft EMET («Enhanced Mitigation Experience Toolkit», «расширенный набор инструментов противодействия») включает ряд полу-экспериментальных средств защиты, которые могут обнаруживать heap spraying или попытки вызова определённых критичных функций в ROP-эксплойтах. Но в непрерывной цифровой войне, даже часть этих приёмов уже побеждена. Это не делает их бесполезными — сложность (а значит и цена) эксплуатации уязвимостей возрастает с каждым применённым средством противодействия — но это напоминание о необходимости постоянной бдительности.
Переполнение буфера. Защита вашего компьютера
Переполнение буфера
Некоторые вирусы и атаки достигают цели без участия пользователя. Несмотря на усилия, интенсивность удаленных атак не снижается, а отражать их становится все труднее. Как это получается? Ведь чтобы программа, пусть и зловредная, что-то сделала, она должна быть кем-то или чем-то запущена. Анализ показывает, что в подавляющем большинстве атак используются ошибки переполнения буфера, и эта проблема является первостепенной.
Впервые данная уязвимость была использована в 1988 году – на ней основывались атаки червем Морриса. С тех пор количество подобных атак увеличивается с каждым годом. В настоящее время можно утверждать, что уязвимости, связанные с переполнением буфера, являются доминирующими при удаленных атаках, где обычный пользователь Сети получает частичный или полный контроль над атакуемым. Приблизительно половина вредоносных программ использует этот тип уязвимости.
В материале «Википедии» (http://ru.wikipedia.org) дано следующее определение данной уязвимости: «Переполнение буфера – это явление, возникающее, когда компьютерная программа записывает данные за пределами выделенного в памяти буфера».
В «Новом словаре хакера» Эрика С. Рэймонда сказано, что «переполнение буфера – это то, что с неизбежностью происходит при попытке засунуть в буфер больше, чем тот может переварить».
Представьте следующую ситуацию. Функция изменения пароля может воспринять пароль длиной не более 256 символов. Чаще всего никто не пользуется паролями длиннее 8–10 символов, поэтому разработчики не предусмотрели проверку строки ввода данных. При попытке ввести более 256 символов, если за данными находился адрес возврата функции, он будет перезаписан и в лучшем случае выполнение программы завершится с ошибкой. Хакеру, обнаружившему такой уязвимый участок, остается подставить в качестве адреса возврата правильный адрес, что переведет управление в любую точку программы на его выбор. В результате может быть выполнен любой произвольный код, который хакер поместил в указанную область памяти, с привилегиями, с которыми выполняется текущая программа.
Подобные ошибки в программном обеспечении находят чуть ли не ежедневно, но не всегда и не сразу устраняют. Для примера можно просмотреть статистику на специализированных сайтах. Согласно данным Secunia (http://secunia.com) в Microsoft Windows XP Professional не устранено 30 из 201 уязвимостей, обнаруженных с 2003 по начало 2008 года, хотя имеющих статус highly critical (предельно опасный), которые позволяют удаленно выполнить код (то есть фактически получить доступ к системе), в этом списке уже нет. В среднем обнаруживается три-четыре уязвимости в месяц.
В Internet Explorer 7 не устранено 7 из 21 найденной уязвимости, и некоторые из них имеют статус highly critical. В среднем в месяц обнаруживается одна-две уязвимости. Если учесть все уязвимости, при которых можно удаленно выполнить код в системе, можно сделать вывод, что с этим браузером вообще опасно выходить в Интернет. Internet Explorer позволяет выполнение кода при обработке HTML Help ActiveX, файлов HTA, SWF, ZIP, BMP и JPEG, архивов FTP, Cookies, тега IFRAME и всплывающих окон, то есть для проникновения в систему троянца достаточно зайти на сайт и просмотреть/сохранить рисунки или архив ZIP.
Внимание!
Найденные уязвимости далеко не всегда устраняются сразу, часто они присутствуют годами.
В операционной системе Windows код Internet Explorer используют и многие другие приложения (почтовый клиент Outlook Express, Проигрыватель Windows Media и др.), отчего увеличивается вероятность поражения. Например, существует JPEG-эксплоит, который использует ошибку в системной библиотеке, возникающую при обработке графических JPEG-файлов. Эта ошибка позволяет заразить компьютер через любую программу – Outlook Еxpress или любой другой почтовый клиент, показывающий JPEG-картинки.
Следует также обратить внимание на возможность вывода в адресной строке Internet Explorer адреса, не соответствующего адресу загруженного сайта (такой вид атаки называется URL-спуфинг), и отображение всплывающих окон, которые, как думает пользователь, принадлежат просматриваемой в браузере странице, а также модификация заголовка окна или информации в строке состояния.
По данным различных источников, именно Internet Explorer сегодня является самым популярным веб-браузером, так как он интегрирован в Windows. Поэтому данная программа будет привлекать внимание злоумышленников, желающих реализовать свои замыслы, ведь вероятность того, что пользователь придет на специально созданный ресурс именно с Internet Explorer, остается самой высокой.
Давайте посмотрим, что на сегодня можно сказать о самом известном из бесплатных браузеров – Mozilla Firefox 2. В нем обнаружено 19 уязвимостей, из которых не устранено четыре. Самая опасная из них имеет статус less critical (ниже критического). Использовав эти уязвимости, получить полное управление компьютером невозможно, злоумышленник может лишь раскрыть некоторую системную информацию, поэтому следует признать, что положение этого браузера лучше, чем Internet Explorer.
Таким образом, оптимальным решением будет использование альтернативных приложений. Вместо Internet Explorer для серфинга можно применять, например, Mozilla Firefox, вместо Outlook Еxpress – The Bat! и т. д. У этих программ ситуация с безопасностью лучше.
Следует также периодически устанавливать обновления и использовать новые версии программ, в которых устранены старые ошибки (возможные новые ошибки – это уже другой вопрос). Правда, размер обновлений подчас до стигает нескольких десятков мегабайт и кроме Internet Explorer и Windows необходимо обновлять другие продукты, следовательно, трафик может быть накладным для бюджета. В таком случае следует ограничиться хотя бы обновлениями, устраняющими критические ошибки.
Наверняка вы задаетесь вопросом: неужели за столько лет никто не пытался бороться с переполнением буфера? Конечно же, пытались. Ведь разработка атакующих программ за несколько десятилетий переросла чистый энтузиазм и приняла коммерческую основу, что говорит о нарастающей опасности.
На борьбу с данной уязвимостью направлены такие утилиты, как, например, Stack-Guard. Первоначально она была разработана для Unix-систем, но в настоящее время ее аналог используется в Microsoft Visual Studio.NET и ProPolice компании IBM. В продуктах компании Microsoft, Windows XP SP2 и Server 2003 SP1 используется технология DEP (Data Execution Protection – защита от выполнения данных), которая делает секцию данных и стек неисполняемыми, что должно предотвращать подобный тип атак.
В некоторых процессорах Intel и AMD имеется специальный бит: в Intel – XD (eXecute Disable – запрет выполнения), в AMD – NX (No eXecute – нет выполнения), позволяющий аппаратно реализовать поддержку DEP.
При отсутствии поддержки на компьютере неисполняемых страниц на аппаратном уровне используется Software-enforced DEP (принудительный программный DEP). Программная защита DEP встраивается во время компиляции и поэтому работает только для пересобранных с поддержкой DEP системных библиотек и приложений.
В этих средствах можно обнаружить и недостатки. При использовании технологии DEP встал вопрос совместимости. Некоторым приложениям требуется исполняемый стек (а таких много: эмуляторы, компиляторы, защитные механизмы и пр.), поэтому по умолчанию защита включена только для системных процессов. Появление новой технологии подстегнуло хакеров, и практически одновременно были представлены методы, позволяющие обойти защиту. Атаковать стало сложнее, но все равно возможно.
Другие описанные механизмы позволяют защититься только от одного типа переполнения буфера, а их существует несколько видов, поэтому считать это полноценной защитой нельзя.
Хотелось бы немного успокоить вас: наличие ошибки не всегда позволяет реализовать атаку в полной мере: буфер может быть мал для внедрения кода (невозможно использование нулевых байтов), адреса системных функций у различных версий могут не совпадать, и человек, пытающийся использовать уязвимость, должен обладать действительно глубокими знаниями.
Данный текст является ознакомительным фрагментом.
Продолжение на ЛитРес
Обнаружено переполнение стекового буфера в Windows 10
В Telegram ко мне обратился один пользователь с ошибкой переполненного стекового буфера в Windows 10. Долго мы решали проблему, а точнее виновника, и нашли. Дело в том, что он майнил криптовалюту сутками и буфер в определенном стеке переполнялся. Я не хотел делать инструкцию так как думал, что это единичный случай, да и криптовалюта не моя стихия, но оказалось нет. Через некоторые время я обнаружил эту ошибку у себя в Discord, и еще один пользователь был с проблемой Twitch. В чем же соль?
Когда конкретная программа записывает большой объем данных в буфер определенного стека, превышающий объем памяти, выделенный для этого стека, то буфер на основе стека переполняется из-за нехватки памяти и вызывает фатальную ошибку. Ошибка также может возникнуть из-за неправильной работы системных файлов, или программы, которая написана кривым кодом. По сути, переполнение буфера считается опасным, в особенности, если это делает какая-либо сторонняя непонятная программа, так как можно делегировать права, и внедрять свой вредоносной код. Если вы оказались в такой ситуации, то разберем ошибку в Windows 10, которая звучит:
Обнаружено переполнение стекового буфера в данном приложении. Это переполнение может позволить злоумышленнику получить управление над данными приложением.
Переполнение стекового буфера в Windows 10 — Что делать?
Вы должны понимать, что переполнение стекового буфера может быть вызвана какой-либо программой или системным процессом. Попробуйте переустановить программу и скачать её с официального сайта, если она загружена с торрента. Вторым действием, воспользуетесь антивирусом и просканируйте всю систему. Запустите защитник Windows 10 и выберите параметр сканирования в автономном режиме. Также, рекомендую воспользоваться антивирусным сканером как Zemana или DrWeb.
1. Переименовать BannerStore
Нажмите сочетание кнопок Win+R и введите regedit, чтобы открыть редактор реестра. В реестре перейдите по пути:
HKEY_CURRENT_USER\Software\Microsoft\Windows \CurrentVersion\Explorer\BannerStore
- Нажмите правой кнопкой мыши по BannerStore и переименуйте его.
Перезагрузите ПК
2. Проверка системных файлов SFC и DISM
Запустите командную строку от имени администратора и введите три команды по очереди, дожидаясь окончание процесса после каждой.
sfc /scannow
DISM /Online /Cleanup-Image /RestoreHealth
DISM /online /Cleanup-Image /ScanHealth
Перезагрузите ПК.
3. Обновление Windows 10
Если у вас критическая ошибка переполнение буферного стека, и вы ничего толком на рабочем столе сделать не можете, так как она мешает своим появлением, и у вас старая версия системы, то можно попробовать обновить Windows 10 через средство Update Assistant.
4. Восстановление системы
Создайте установочную флешку с Windows 10 и начните установку системы. Когда дойдете до пункта установки, то нажмите снизу на «Восстановление системы«.
Вас перебросит в дополнительные параметры загрузки, где вы можете попробовать «Восстановление системы», «Восстановление при загрузке» или выбрать «Параметры загрузки», чтобы запустить безопасный режим.
Если вы каким-либо способом решили проблему с переполнением буферного стека в Windows 10, то сообщите в комментариях, как решили, и что вызвало, чтобы помочь остальным пользователям в данной проблеме.
Загрузка комментариев
Переполнение буфера — это… Что такое Переполнение буфера?
Переполнение буфера (Buffer Overflow) — явление, возникающее, когда компьютерная программа записывает данные за пределами выделенного в памяти буфера.
Переполнение буфера обычно возникает из-за неправильной работы с данными, полученными извне, и памятью, при отсутствии жесткой защиты со стороны подсистемы программирования (компилятор или интерпретатор) и операционной системы. В результате переполнения могут быть испорчены данные, расположенные следом за буфером (или перед ним).
Переполнение буфера является наиболее популярным способом взлома компьютерных систем, так как большинство языков высокого уровня используют технологию стекового кадра — размещение данных в стеке процесса, смешивая данные программы с управляющими данными (в том числе адреса начала стекового кадра и адреса возврата из исполняемой функции).
Переполнение буфера может вызывать аварийное завершение или зависание программы, ведущее к отказу обслуживания (denial of service, DoS). Отдельные виды переполнений, например переполнение в стековом кадре, позволяют злоумышленнику загрузить и выполнить произвольный машинный код от имени программы и с правами учетной записи, от которой она выполняется.
Известны примеры, когда переполнение буфера намеренно используется системными программами для обхода ограничений в существующих программных или программно-аппаратных средствах. Например, операционная система iS-DOS (для компьютеров ZX Spectrum) использовала возможность переполнения буфера встроенной TR-DOS для запуска своего загрузчика в машинных кодах (что штатными средствами в TR-DOS сделать невозможно).
Безопасность
Программа, которая использует уязвимость для разрушения защиты другой программы, называется эксплойтом. Наибольшую опасность представляют эксплойты, предназначеные для получения доступа к уровню суперпользователя или, другими словами, повышения привилегий. Эксплойт переполнения буфера достигает этого путём ввода специально изготовленных входных данных. Такие данные переполняют выделенный буфер и изменяют данные, которые следуют за этим буфером в памяти.
Представим гипотетическую программу системного администрирования, которая исполняется с привилегиями суперпользователя — к примеру, изменение паролей пользователей. Если программа не проверяет длину введённого нового пароля, то любые данные, длина которых превышает размер выделенного для их хранения буфера, будут просто записаны поверх того, что находилось после буфера. Злоумышленник может вставить в эту область памяти инструкции на машинном языке (шелл-код), выполняющие любые действия с привилегиями суперпользователя — добавление и удаление учётных записей пользователей, изменение паролей, изменение или удаление файлов и т. д. Если в дальнейшем программа передаст управление в эту область памяти, система исполнит находящийся там машинный код злоумышленника.
Правильно написанные программы должны проверять длину входных данных, чтобы убедиться, что они не больше, чем выделенный буфер данных. Однако программисты часто забывают об этом. В случае если буфер расположен в стеке и стек «растёт вниз» (например в архитектуре x86), то с помощью переполнения буфера можно изменить адрес возврата выполняемой функции, так как адрес возврата расположен после буфера, выделенного выполняемой функцией. Тем самым есть возможность выполнить произвольный участок машинного кода в адресном пространстве процесса. В случае же, если стек «растёт вверх» (в этом случае адрес возврата обычно находятся перед буфером), использовать переполнение буфера для искажения адреса возврата возможно в очень редких случаях[источник не указан 30 дней].
Даже опытным программистам бывает трудно определить, насколько то или иное переполнение буфера может быть уязвимостью. Это требует глубоких знаний об архитектуре компьютера и о целевой программе. Было показано[кем?], что даже настолько малые переполнения, как запись одного байта за пределами буфера, могут представлять собой уязвимости.
Переполнения буфера широко распространены в программах, написанных на относительно низкоуровневых языках программирования, таких как язык ассемблера, Си и C++, которые требуют от программиста самостоятельного управления размером выделяемой памяти. Устранение ошибок переполнения буфера до сих пор является слабо автоматизированным процессом. Системы формальной верификации программ не очень эффективны при современных языках программирования.
Многие языки программирования, например, Java и Lisp, управляют выделением памяти автоматически, и используют комбинацию статического анализа и проверки корректности действий программы во время выполнения. Это делает ошибки, связанные с переполнением буфера, маловероятными или невозможными. Perl для избежания переполнений буфера обеспечивает автоматическое изменение размера массивов. Однако системы времени выполнения и библиотеки для таких языков всё равно могут быть подвержены переполнениям буфера, вследствие возможных внутренних ошибок в реализации этих систем проверки. В Windows доступны некоторые программные решения, которые предотвращают выполнение кода за пределами переполненного буфера, если такое переполнение было осуществлено. Среди этих решений — DEP в Windows XP SP2, OSsurance и Anti-Execute.
Краткое техническое изложение
Описание
Рассмотрим более подробно случай переполнения буфера, расположенного в области стека. Это удобнее всего сделать с помощью примера программы на языке Си. Пример ориентирован на архитектуру x86.
Когда динамический буфер, представляющий собой автоматический массив, выделяется в функции, он создаётся на стеке во время вызова этой функции. В архитектуре x86 стек растёт от бо́льших адресов к меньшим (или справа налево, в приведённых ниже диаграммах), то есть новые данные помещаются перед теми, которые уже находятся в стеке. Здесь, (DATA) (DATA) (…)
представляет существующий стек, и (NEWDATA)
— это некоторое новое значение, которое ЦП поместил в стек:
(NEWDATA)(DATA)(DATA)(...)
Записывая данные в буфер, можно осуществить запись за его границами и изменить находящиеся там данные. Когда программа вызывает подпрограмму, она помещает адрес возврата в стек, так что подпрограмма знает, куда возвращать управление после того, как она завершится:
(ADDR)(DATA)(DATA)(...)
Когда выделяется динамический буфер, стек растёт влево на размер буфера. Так, если функция начинается с объявления char a[10]
, результатом будет:
(.a........)(ADDR)(DATA)(DATA)(...)
В конце подпрограммы память, занятая буфером, освобождается, и вызывается операция RET
. Она извлекает адрес возврата из стека и выполняет переход по этому адресу, возвращая управление туда, откуда была вызвана подпрограмма.
Предположим, что 10-байтный буфер предназначен для того, чтобы содержать данные, предоставляемые пользователем (например — пароль). Если программа не проверяет количество символов, которые были введены пользователем, и записывает 14 байт в буфер, эти лишние данные будут помещены поверх адреса возврата. Таким образом, это изменит адрес, по которому будет передано управление, когда завершится подпрограмма, и с которого программа продолжит исполнение после этого.
Если пользователь не злонамерен и вводит более, чем 10 символов, добавочные данные будут скорее всего случайными. В таком случае вполне возможно, что адрес возврата будет указывать на область памяти, которая неподконтрольна текущей исполняемой программе. Это вызовет ошибку сегментации в UNIX-системах или аналогичную ошибку в других операционных системах.
Однако пользователь может подставить в качестве адреса возврата и некий правильный адрес. Это вызовет переход управления в любую точку программы по его выбору. В результате потенциально может быть выполнен любой произвольный код, который этот пользователь поместил в данную область памяти, с теми привилегиями, с которыми выполняется текущая программа.
Пример
Рассмотрим следующую программу на языке Си. Скомпилировав эту программу, мы сможем использовать её для генерации ошибок переполнения буфера. Первый аргумент командной строки программа принимает как текст, которым заполняется буфер.
/* overflow.c - демонстрирует процесс переполнения буфера */ #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char buffer[10]; if (argc < 2) { fprintf(stderr, "ИСПОЛЬЗОВАНИЕ: %s строка\n", argv[0]); return 1; } strcpy(buffer, argv[1]); return 0; }
Программу можно опробовать с несколькими разными строками. Строки размером в 9 или меньше символов не будут вызывать переполнение буфера. Строки в 10 и более символов будут вызывать переполнение, хотя это может и не приводить к ошибке сегментации.
Эта программа может быть переписана следующим образом, с использованием функции Strncpy для предотвращения переполнения. Однако, следует учитывать, что простое отбрасывание лишних данных, как в этом примере, также может приводить к нежелательным последствиям, в том числе, при определённых условиях, к повышению привилегий. Как правило, требуется более тщательная обработка таких ситуаций.
/* better.c - демонстрирует, как исправить ошибку */ #include <stdio.h> #include <string.h> #define BUFFER_SIZE 10 int main(int argc, char *argv[]) { char buffer[BUFFER_SIZE]; if (argc < 2) { fprintf(stderr, "ИСПОЛЬЗОВАНИЕ: %s строка\n", argv[0]); return 1; } strncpy(buffer, argv[1], BUFFER_SIZE); return 0; }
Предотвращение
Для того, чтобы сделать переполнение буфера менее вероятным, используются различные приёмы.
Системы обнаружения вторжения
С помощью систем обнаружения вторжения (СОВ) можно обнаружить и предотвратить попытки удалённого использования переполнения буфера. Так как в большинстве случаев данные, предназначенные для переполнения буфера, содержат длинные массивы инструкций No Operation (NOP
или NOOP
), СОВ просто блокирует все входящие пакеты, содержащие большое количество последовательных NOP-ов. Этот способ, в общем, неэффективен, так как такие массивы могут быть записаны с использованием разнообразных инструкций языка ассемблера. В последнее время крэкеры начали использовать шелл-коды с шифрованием, самомодифицирующимся кодом, полиморфным кодом и алфавитно-цифровым кодом, а также атаки возврата в стандартную библиотеку для проникновения через СОВ.
Защита от повреждения стека
Защита от повреждения стека используется для обнаружения наиболее частых ошибок переполнения буфера. При этом проверяется, что стек вызовов не был изменён перед возвратом из функции. Если он был изменён, то программа заканчивает выполнение с ошибкой сегментации.
Существуют две системы: StackGuard и Stack-Smashing Protector (старое название — ProPolice), обе являются расширениями компилятора gcc. Начиная с gcc-4.1-stage2, SSP был интегрирован в основной дистрибутив компилятора. Gentoo Linux и OpenBSD включают SSP в состав распространяемого с ними gcc.
Размещение адреса возврата в стеке данных облегчает задачу осуществления переполнения буфера, которое ведёт к выполнению произвольного кода.X, которая также обеспечивает контроль исполняемого пространства.
Заметим, что этот способ защиты не предотвращает повреждение стека. Однако он часто предотвращает успешное выполнение «полезной нагрузки» эксплойта. Программа не будет способна вставить код оболочки в защищённую от записи память, такую как существующие сегменты исполняемого кода. Также будет невозможно выполнение инструкций в неисполняемой памяти, такой как стек или куча.
ASLR затрудняет для взломщика определение адресов функций в коде программы, с помощью которых он мог бы осуществить успешную атаку, и делает атаки типа ret2libc очень трудной задачей, хотя они всё ещё возможны в контролируемом окружении, или если атакующий правильно угадает нужный адрес.
Некоторые процессоры, такие как Sparc фирмы Sun, Efficeon фирмы Transmeta, и новейшие 64-битные процессоры фирм AMD и Intel предотвращают выполнение кода, расположенного в областях памяти, помеченных специальным битом NX. AMD называет своё решение NX (от англ. No eXecute), а Intel своё — XD (от англ. eXecute Disabled).
Защита пространства исполняемого кода для Windows
Сейчас существует несколько различных решений, предназначенных для защиты исполняемого кода в системах Windows, предлагаемых как компанией Майкрософт, так и сторонними компаниями.
Майкрософт предложила своё решение, получившее название DEP (от англ. Data Execution Prevention — «предотвращение выполнения данных»), включив его в пакеты обновлений для Windows XP и Windows Server 2003. DEP использует дополнительные возможности новых процессоров Intel и AMD, которые были предназначены для преодоления ограничения в 4 ГиБ на размер адресуемой памяти, присущий 32-разрядным процессорам. Для этих целей некоторые служебные структуры были увеличены. Эти структуры теперь содержат неиспользуемый (зарезервированный) бит NX. DEP использует этот бит для предотвращения атак, связанных с изменением адреса обработчика исключений (так называемый SEH-эксплойт). DEP обеспечивает только защиту от SEH-эксплойта, он не защищает страницы памяти с исполняемым кодом.
Кроме того, Майкрософт разработала механизм защиты стека, предназначенный для Windows Server 2003. Стек помечается с помощью так называемых «осведомителей» (англ. canary), целостность которых затем проверяется. Если «осведомитель» был изменён, значит, стек повреждён.
Существуют также сторонние решения, предотвращающие исполнение кода, расположенного в областях памяти, предназначенных для данных или реализующих механизм ASLR.
Использование безопасных библиотек
Проблема переполнений буфера характерна для языков программирования Си и C++, потому что они не скрывают детали низкоуровневого представления буферов как контейнеров для типов данных. Таким образом, чтобы избежать переполнения буфера, нужно обеспечивать высокий уровень контроля за созданием и изменениями программного кода, осуществляющего управление буферами. Использование библиотек абстрактных типов данных, которые производят централизованное автоматическое управление буферами и включают в себя проверку на переполнение — один из инженерных подходов к предотвращению переполнения буфера.
Два основных типа данных, которые позволяют осуществить переполнение буфера в этих языках — это строки и массивы. Таким образом, использование библиотек для строк и списковых структур данных, которые были разработаны для предотвращения и/или обнаружения переполнений буфера, позволяет избежать многих уязвимостей.
См. также
Ссылки
Библиотеки и другие средства защиты
Как исправить ошибку переполнения стекового буфера
Среди многочисленных проблем программного характера, возникающих при работе с компьютером, пользователям может встречаться ошибка, сообщающая об обнаружении переполнения стекового буфера в конкретном приложении и возможности получения злоумышленником управления данными софта. Этому багу уже десятки лет, но и сегодня разрабатываемые программы не могут похвастать абсолютной надёжностью. Переполнение стековой памяти может возникать у любого неидеально продуманного приложения, что влечёт за собой аварийное закрытие или зависание софта, а также позволяет злоумышленнику внедрить вредоносный код, выполняемый от имени уязвимой программы. Если при этом приложение выполняется с наивысшими привилегиями, это открывает путь взломщику к любым манипуляциям в системе.
Бывает, что переполнение буфера при программировании продукта является средством, служащим определённым целям, например, намеренно применяется системным софтом для обхода отдельных ограничений. Рассмотрим подробнее, что это за явление, почему возникает и как избавиться от системной ошибки.
Причины возникновения ошибки переполнения стекового буфера
Для размещения данных программами используются блоки памяти (буферы), обычно фиксированной длины, то есть вмещающие ограниченный объём информации. Ошибка переполнения стекового буфера возникает, когда приложение пишет больше данных, чем выделено под стековый буфер, провоцируя перезаписывание, и не исключено, что будут перезаписаны важные избыточные данные в стеке, расположенные следом за массивом или перед ним.
Стек (абстрактный тип данных) являет собой список элементов, располагающихся стопкой, где информация упорядочена таким образом, что добавление элемента делает его головным, а удаление убирает первый элемент, тогда как головным станет следующий за ним. Принцип работы стека часто сравнивается со стопкой тарелок – выдернуть из середины тарелку нельзя, снимаются они поочерёдно, начиная с верхней, то есть порядок взаимодействия осуществляется по принципу LIFO (Last In, First Out – последним пришёл, первым ушёл).
Такое явление как переполнение буфера, когда программа захватывает больше данных, чем выделенный под них массив, в лучшем случае при ошибочном переполнении приводит к отказу софта или некорректной работе. В худшем, это будет означать, что уязвимость может быть применена в целях вредительства. Переполнение в стековом кадре используется злоумышленниками для изменения адреса возврата выполняемой функции, открывая возможности управления данными, независимо от того, буфер расположен в стеке, который растёт вниз, и адрес возврата идёт после буфера, или же стек растёт вниз, и адрес возврата находится перед буфером. Реализовать такое поведение программы несложно с применением вредоносного кода. С блоками памяти определённого размера компьютер работает в любых приложениях или процессах.
Так, в своих целях применять переполнение стекового буфера могут сетевые черви или иной вредоносный софт. Особенно опасными являются эксплойты, использующие уязвимость, которые предназначаются для получения привилегий путём передачи программе намеренно созданных входных данных, повреждающих стек. Эти данные переполняют буфер и меняют данные, следующие в памяти за массивом.
Скачивание сомнительного, взломанного программного обеспечения, включая пиратские сборки Виндовс, всегда таит в себе определённые риски, поскольку содержимое может хранить вредоносный код, выполняющийся при установке софта на компьютер.
Что делать, если обнаружена уязвимость в данном приложении
Первое, что нужно сделать в том случае, когда ошибка проявилась в конкретной программе, это попробовать её переустановить, загрузив инсталлятор из проверенного источника, лучше официального. Перед инсталляцией софта следует убедиться в его безопасности, просканировав антивирусом, особенно внимательно нужно устанавливать ПО при пакетной установке, когда в довесок к скачиваемому продукту идут и дополнительные элементы, часто вредоносные или просто ненужные. Переустановка софта и перезагрузка компьютера избавляют от ошибки, если она была случайной.
Рассмотрим, несколько способов, как исправить ошибку, если произошло переполнение стекового буфера Windows 10.
Использование антивирусного ПО
В тексте ошибки переполнения буфера говорится о потенциальной угрозе безопасности, и, несмотря на достаточно преклонный возраст и известность бага, он всё ещё актуален и нередко становится средством взлома систем. Причём сбою поддаются программы различных типов, а спровоцировать его можно специально задействованным вредоносным софтом.
Рекомендуется просканировать систему на вирусы, можно в безопасном режиме, если ОС не загружается, и выполнить проверку и устранение угроз посредством встроенного Защитника Windows.
Как очистить компьютер от вирусов при появлении ошибки «Стековый буфер переполнен»:
- Открываем Защитник Windows через поисковую строку меню «Пуск» или в области уведомлений на панели задач;
- Выбираем «Защита от вирусов и угроз» и переходим к параметрам сканирования;
- Отмечаем флажком «Автономное сканирование Защитника Windows» и жмём соответствующую кнопку для начала проверки.
Чистая загрузка ОС Windows
Если переустановка софта и перезагрузка не помогли, и ошибка переполнения стекового буфера не исчезла, стоит попробовать выполнить чистую загрузку системы. Возможно, причины проблемы не относятся к данному приложению, ведь кроме работающих программ в Windows запущен ряд прочих процессов, которые и могут провоцировать баг. Для выполнения загрузки ОС в чистом виде нужно войти под учётной записью администратора компьютера, некоторые функции и приложения при этом будут недоступны, поскольку в данном режиме запускаются только необходимые системе файлы.
Для чистой загрузки Windows выполняем следующие действия:
- Открываем консоль «Выполнить» (Win+R), вводим в поле команду msconfig, жмём «Ок» или клавишу Enter.
- В окне «Конфигурация системы» на вкладке «Общие» снимаем отметку с пункта «Загружать элементы автозагрузки». Затем на вкладке «Службы» отмечаем пункт «Не отображать службы Майкрософт» и жмём кнопку «Отключить все».
- Идём на вкладку «Автозагрузка» и жмём ссылку «Открыть диспетчер задач» (для Windows 10), в открывшемся окне Диспетчера задач поочерёдно отключаем каждую программу в списке.
- Возвращаемся к окну конфигурации и жмём «Ок», после чего перезагружаемся и проверяем, исчезла ли ошибка.
Для того чтобы выявить программу, ставшую причиной проблемы, нужно включать софт по одному в автозагрузке и службах, после чего выполнять перезагрузку.
Специализированный софт
В сети есть немало лечащих утилит (Dr.Web CureIt, Kaspersky и др.), способных избавить компьютер от вирусов. Портативные программы не будут конфликтовать с уже установленным антивирусом и эффективно выполнят задачу сканирования и удаления вредоносного ПО. Есть также антивирусный софт, способный решать проблему на низком уровне, если вирусы не дают системе запуститься. Используя утилиты с обновлённой вирусной базой, можно исправить, в том числе ошибку переполнения стекового буфера.
Восстановление Windows
Ещё одна мера, позволяющая избавится от системной ошибки, предполагает выполнение восстановления системы. Для использования функции потребуется наличие заранее созданного накопителя восстановления Windows, в качестве которого можно использовать диск или флешку. Выполняем следующие действия:
- отключаем от компьютера лишние устройства, не требуемые для работы;
- вставляем загрузочный накопитель и загружаемся с него, предварительно выставив приоритет загрузки в BIOS;
- выбираем «Восстановление системы» – «Диагностика» – «Дополнительные параметры» – «Восстановление при загрузке», далее выбираем ОС, которую требуется восстановить, и ждём окончания процесса, перезагружаемся.
Крайней мерой, когда более простые и гуманные способы решения не помогли исправить ошибку, является переустановка Windows.
Переполнение буфера в Siemens Simatic WinCC и PCS 7
Уязвимое ПО
Siemens Simatic WinCC
Версия: 7.0 SP3 и более ранняя
Siemens Simatic PCS 7
Версия: 8.0 и более ранняя
Ссылка:
http://www.siemens.com/
Рейтинг опасности
Уровень опасности: Средний
Воздействие: Выполнение произвольного кода
Вектор атаки: Удаленный
CVSS v2:
Base Score: 6.8
Vector: (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVE: CVE-2013-0674
Описание программы
Simatic WinCC входит в пакет программного обеспечения Siemens SIMATIC HMI, предназначенного для обеспечения взаимодействия между оператором и программируемыми логическими контроллерами (PLCs), управляющими технологическими процессами.
Описание уязвимости
Специалисты Исследовательского центра Positive Research компании Positive Technologies обнаружили уязвимость «Переполнение буфера» в Siemens Simatic WinCC и PCS 7.
Компонент «RegReader» управляющего элемента ActiveX, встроенный в веб-браузер пользователя, подвержен атакам типа «переполнение буфера». Злоумышленник может получить контроль над ПК, заставив пользователя перейти на вредоносный веб-сайт, который обращается к вышеуказанному компоненту управляющего элемента ActiveX и вызывает переполнение буфера.
Решение
Установите последнюю версию приложения
Статус уведомления
02.08.2012 — Производителю отправлены детали уязвимости
15.03.2013 — Производитель выпустил исправление
29.03.2013 — Публикация уязвимости
Благодарности
Уязвимость обнаружили Глеб Грицай, Дмитрий Нагибин (Исследовательский центр Positive Research компании Positive Technologies)
Ссылки
http://www.securitylab.ru/lab/PT-2013-28
http://www.siemens.com/corporate-technology/pool/de/forschungsfelder/siemens_security_advisory_ssa-714398.pdf
Список отчетов о ранее обнаруженных уязвимостях Positive Research:
http://www.ptsecurity.ru/lab/advisory/
http://www.securitylab.ru/lab/
Атака с использованием переполнения буфера Текст научной статьи по специальности «Компьютерные и информационные науки»
УДК 658.012
В.Н. ГУГНИН, ст. преп. НТУ «ХПИ» (г. Харьков),
Д.В. СОТНИК, вед. программист ЗАО «ИИТ Циклон» (г. Харьков)
АТАКА С ИСПОЛЬЗОВАНИЕМ ПЕРЕПОЛНЕНИЯ БУФЕРА
В статті розглядаються методи підвищення прав користувача та виконання довільного коду з використанням переповнення буферу. Зроблені висновки та наведені рекомендації по ліквідації та упередженню існуючих та потенційних помилок операційної системи що можуть становити загрозу безпеці робочої станції.
In present article were overviewed methods for user rights raising and arbitrary code launching with buffer overflow using. Adducted some conclusion and recommendations about liquidation and forestalling existing and potential operation system errors, which can threat workstation safety.
Постановка проблемы. Переполнение буфера (buffer overflows) -название самой распространенной уязвимости в области безопасности программного обеспечения. Первая атака с применением данной уязвимости использовалась в вирусе-черве Морриса в 1988 году. С тех пор их число увеличивается с каждым годом. В настоящее время можно говорить, что уязвимости, связанные с переполнение буфера, являются доминирующими при удаленных атаках, где обычный пользователь сети получает частичный или полный контроль над атакуемым узлом. Анализ атак и обнаруженных уязвимостей последних лет показывает, что данная проблема является первостепенной.
Анализ литературы. В 1998 году 9 из 13 выпусков CERT (Computer Emergency Response Team site) и по крайней мере половина выпусков 1999 года связаны с переполнением буфера. Отметим, что переполнение буфера присуще также программному обеспечению ряда аппаратных средств. Примером может служить уязвимость принтера HP LaserJet 4500.
Большое количество как идеологических, так и ошибок реализации программного обеспечения делает современные вычислительные системы уязвимыми для повреждения, копирования и изменения хранимых данных [1]. Детальный анализ существующих уязвимостей указывает на то, что внимание безопасности при разработке некоторых сетевых протоколов не уделялось совсем, а соответствующие средства в программные модули встраивались при обнаружении уязвимости [2].
Детальный анализ современных удаленных атак и эпидемий компьютерных вирусов, таких как Lovesun, Mydoom и т.д. (3 самых разрушительных эпидемии за все время в течении последнего года) показал что все массовые и наиболее серьезные и опасные удаленные и локальные атаки строятся на использовании переполнения буфера. Наиболее серьезная уязвимость 2004 года, отнесенная Microsoft к типу Critical, с использованием
52
которой были связаны 3 наиболее крупных вирусных эпидемии и миллионы взломов рабочих станций была связана с переполнением буфера в RPC DCOM модуле [3]. Однако до сих пор отсутствует классификация описанных атак и способов противодействия по типу, в зависимости от особенностей выполнения внедрения кода.
Цель статьи. В данной статье рассматриваются методы повышения прав пользователя в системе при помощи выполнения произвольного кода с использованием переполнения буфера, как в фрагментах операционной системы, так и в используемом прикладном программном обеспечении. Вводится классификация типов атак с использованием переполнения буфера, рассматриваются способы защиты буфера на этапе разработки приложений.
Основной раздел. Основа атак с использованием этой уязвимости -принцип функционирования операционных систем, где программа получает привилегии и права запустившего ее пользователя или процесса. Таким образом, менее привилегированный пользователь или процесс, который взаимодействует с данной программой, может использовать ее права в своих целях. «Переполнение буфера» делает это возможным. Использование данной уязвимости подразумевает изменение хода выполнения привилегированной программы, например, запуск командной оболочки с правами администратора [4]. Реализации атаки требует решения двух подзадач:
• Подготовка кода, который будет выполнятся в контексте привилегированной программы.
• Изменение последовательности выполнения программы с передачей управления подготовленному коду.
Рассмотрим пути решения подзадачи подготовки кода:
• Подготавливаемый код представляет собой машинные инструкции соответствующего процессора и может передаваться в программу в качестве ее параметра или команды. При этом параметр или команда сохраняется программой в отведенном для этого буфере. Буфер может находится в любой области памяти: в стеке (локальные, автоматические переменные), в динамически распределяемой памяти, в области статических данных. Например, программе, запрашивающей ввод строки, под видом строки может быть передан нужный атакующему код, которому в дальнейшем будет передано управление [5].
• Нужный код не передается в программу, так как он уже присутствует в ней самой или в ее адресном пространстве и требуется лишь его параметризация. Например, подготовка параметра для функции запуска программы. В данном случае атакующему требуется
изменить или сформировать нужный параметр, а не сам код. Параметр также может находится в любой области памяти.
• Если параметризированный код уже присутствует в адресном пространстве программы, то подготовки кода не требуется.
Далее рассмотрим способы передачи управления подготовленному коду. В основе этих способов лежит переполнение буфера, т. е. блока памяти, выделенного под переменную. Переполнение возникает при отсутствии проверки выхода за границы буфера. Таким образом, искажается содержимое других переменных состояния и параметров программы, которые входят в область переполнения буфера. Типы искажаемых объектов -переменных определяет способ передачи управления коду атакующего и могут быть следующими:
Искажение адреса возврата из функции.
Так как вызову функции сопутствует занесение адреса возврата в стек, то при его подмене атакующим, управление передается по заданному им адресу. Здесь используется переполнение буфера локальных переменных функции, которые также создаются в стеке. Частью переменной будет замещен адрес возврата из функции в стеке. Далее, при выполнении инструкции возврата из подпрограммы, управление будет передано по адресу, который образуют соответствующие позиции перезаписанного участка памяти и, в обычной ситуации, будет получено сообщение об ошибке операционной системы. Такие атаки на переполнение буфера получили название «атаки срыва стека» (stack smashing attack).
Искажение указателя функции.
В данном случае атаке подвергаются переменные, содержащие указатели на функции. Эти переменные могут располагаться в любой области памяти, не только в стеке но и в области динамически и статически выделяемых данных. Атакующий организовывает переполнение буфера, которое искажает данные указатели, и при вызове функций по этим указателям управление передается подготовленному коду. Комбинация всех методов подготовки кода и целей переполнения буфера (типа искажаемых структур) определяет виды всех возможных атак по переполнению буфера.
Переполнение буфера происходит, прежде всего, из-за неправильного алгоритма работы программы, который не предусматривает проверок выхода за границы буферов. Также особую роль здесь играет язык программирования Си и его стандартные библиотеки. Так как Си не содержит средств контроля соответствия типов, то в переменную одного типа
можно занести значение другого типа, перезаписывая участок за пределами памяти, отведенной под переменную-назначение.
Таблица
Классификация атак по переполнению буфера [6]
Подготовка кода. Цель переполнения Внедрение кода Внедрение параметров
Искажение адреса возврата из функции Атака «срыв стека» Атака «срыв стека» с параметризацией
Искажение указателей функций Атака на указатели функций Атака на указатели функций с параметризацией
Искажение таблиц переходов Атака на таблицы переходов Атака на таблицы переходов с параметризацией
Искажение указателей данных Атака с искажением указателей данных Атака с искажением указателей данных с параметризацией
Сложившийся годами стиль программирования, более ориентированный на производительность программ, без выполнения дополнительных проверок, как на этапе проектирования, так и на этапе тестирования, также является причиной распространения данной уязвимости. Рассмотрим способы защиты на этапе проектирования:
Использование неисполнимых буферов.
Суть метода заключается в запрещении исполнения кода в сегментах данных и стека, т.е. параметры сегментов данных и стека содержат только атрибуты записи и чтения, но не исполнения. Например, для реализации неисполняемого стека существуют «заплаты» для ОС Solaris и Linux. Однако ограничение на исполнение данных приводит к проблеме несовместимости. Исполняемый стек необходим для работы многим программам, так как на его основе генерируется код компиляторами, реализуются системные функции операционных систем, реализуется автоматическая генерация кода. Защита с использованием неисполнимых буферов предотвратит только атаки с внедрением кода, но не поможет при других видах атак.
В основе данного метода лежит выполнение проверок выхода за границы переменной при каждом обращении к ней. Это предотвращает все возможные атаки по переполнению буфера, так как полностью исключает само переполнение. Проверки выхода за границы переменной опционально реализованы в некоторых компиляторах Си, например, Compaq C, cc в Tru64 Unix, cc в Alpha Linux. Следует отметить, что реализованные проверки ограничены только точными ссылками на элементы массивов, но не производятся для указателей. Однако, у этого решения есть существенный недостаток — значительное (до 30 раз) снижение производительности программы. Другие системы осуществляют проверки при доступе к памяти, выполняя вставки дополнительного объектного кода проверок во все места программы, где есть обращения к памяти. Вставки могут производится как до сборки объектных файлов (Purify) так и после (Pixie). Такие проверки сказываются на производительности с ее уменьшением от 2 до 5 раз и скорее подходят для отладки.
Применение проверок целостности.
Решение, основанное на данном методе, получено благодаря проекту Synthetix [7]. Цель Synthetix — специализация кода для увеличения производительности операционных систем. При этом вводится понятие так называемого квази-постоянства (Quasi-invariant), т.е. состояния среды, которое неизменно в определенных рамках. Такое квази-постоянство позволяет устранить ряд избыточного кода проверки выполнения различных условий. В рамках проекта реализован набор утилит, в том числе обеспечивающих контроль и защиту квази-постоянных состояний среды. К их числу относятся StackGuard и PointGuard. StackGuard предназначен для защиты от всех атак по переполнению буфера с изменением адреса возврата из функции и реализован в виде «заплаты» к gcc. Данная заплата изменяет пролог и эпилог всех функций с целью проверки целостности адреса возврата из функции при помощи так называемого «canary word».
Измененный пролог каждой функции выполняет занесение в стек «canary word», а эпилог проверку содержимого стека, занесенного ранее и, в случае нарушения, останавливает программу с предупреждающим сообщением. При атаке с искажением адреса возврата неизбежно произойдет искажение «canary word», что и будет признаком нарушения целостности. При известном значении «canary word» атакующий может организовать подмену адреса возврата без нарушения целостности. Поэтому «canary word» формируется StackGuard особым образом.
Для предотвращения подмены адреса возврата в прологе каждой функции можно выполнять сохранение этого адреса во вторичном (дополнительном) стеке, а в эпилоге восстанавливать его значение. В случае переполнения буфера и искажения адреса возврата он будет восстановлен эпилогом без выдачи дополнительных сообщений, что впоследствии может привести к аварийному завершению. Атака с подменой указателей функций пресекается путем вставки специального кода перед каждой инструкцией вызова подпрограммы по указателю. Специальный код выполняет проверку того, в каком сегменте расположен адрес, вызываемой подпрограммы. Если это область данных или стека, то программа завершается с ненулевым кодом ошибки. Однако, при такой схеме защиты встает проблема несовместимости с программами, которые содержат исполняемый код в области данных и стека.
Выводы. Рассмотренные методы противодействия атакам по переполнению буфера не выполняют полную автоматическую защиту от всех возможных атак. Ряд атак с искажением указателей данных носят логический характер и не могут быть выявлены в автоматическом режиме. Самая первая атака по переполнению буфера в вирусе-черве Морриса носила именно такой характер. Следует обратить внимание на языки, обеспечивающие проверку и сохранение типов, такие как Java, исключающие переполнение буфера. Однако, не следует забывать, что виртуальная машина Java написана на Си и, таким образом, может иметь уязвимости [8].
Список литературы: 1. Чекмарева А.Н., Вишнякова Д.Б. Microsoft (R) Windows 2000: Server и Professional. Русские версии. — СПб.: БХВ — Санкт-Петербург, 2000. — 1056 с. 2. Семенов Ю.А. Протоколы Мете1Энциклопедия. — М.: Горячая линия — Телеком, 2001. — 1100 с. 3. Crispin Cowan, Perry Wagle. Buffer Overflows: Attacks and Defenses for the Vulnerability of the Decade. -Department of Computer Science and Engineering, Oregon Graduate Institute of Science & Technology, 2003. — 235 p. 4. Тодд В. Мазерс. Энциклопедия Windows 2000 для системного администратора. -СПб: Издательский дом «Вильямс», 2001. — 412 с. 5. Вильямс А. Системное программирование в Windows 2000. — СПб.: «ПИТЕР», 2000. — 250 с. 6. SteveBellovin. Buffer Overflows and Remote Root Exploits. Personal Communications, 1999. — 150 p. 7. Семененко В.А., Величкин АМ., Ступин Ю.В. Операционные системы. — М.: Высшая школа, 1990. — 192 с. 8. Саркисян А.А.
Машинонезависимая оптимизация исходных программ. — М.: Радио и связь, 1985. — 208 с.
Поступила в редакцию 29.03.04
Что такое переполнение буфера | Типы атак и методы предотвращения
Что такое переполнение буфера
Буферы — это области памяти, в которых временно хранятся данные во время их передачи из одного места в другое. Переполнение буфера (или переполнение буфера) происходит, когда объем данных превышает емкость буфера памяти. В результате программа, пытающаяся записать данные в буфер, перезаписывает соседние ячейки памяти.
Например, буфер для учетных данных может быть спроектирован так, чтобы ожидать ввода имени пользователя и пароля размером 8 байтов, поэтому, если транзакция включает в себя ввод размером 10 байтов (то есть на 2 байта больше, чем ожидалось), программа может записать избыточные данные за границей буфера.
Переполнение буфера может повлиять на все типы программного обеспечения. Обычно они возникают из-за неправильных входных данных или невозможности выделить достаточно места для буфера. Если транзакция перезаписывает исполняемый код, это может привести к тому, что программа будет вести себя непредсказуемо и генерировать неверные результаты, ошибки доступа к памяти или сбои.
Пример переполнения буфера
Что такое атака переполнения буфера
Злоумышленники используют проблемы переполнения буфера, перезаписывая память приложения.Это изменяет путь выполнения программы, вызывая ответ, который повреждает файлы или раскрывает личную информацию. Например, злоумышленник может ввести дополнительный код, посылая новые инструкции приложению для получения доступа к ИТ-системам.
Если злоумышленники знают структуру памяти программы, они могут намеренно подавать ввод, который буфер не может сохранить, и перезаписывать области, содержащие исполняемый код, заменяя его своим собственным кодом. Например, злоумышленник может перезаписать указатель (объект, указывающий на другую область памяти) и указать его на полезную нагрузку эксплойта, чтобы получить контроль над программой.
Типы атак на переполнение буфера
Переполнение буфера на основе стека более распространено и использует стековую память, которая существует только во время выполнения функции.
Атаки на основе кучи труднее выполнить и включают переполнение пространства памяти, выделенного для программы, за пределы памяти, используемой для текущих операций времени выполнения.
Какие языки программирования более уязвимы?
C и C ++ — это два языка, которые очень восприимчивы к атакам переполнения буфера, поскольку у них нет встроенных средств защиты от перезаписи или доступа к данным в своей памяти.Mac OSX, Windows и Linux используют код, написанный на C и C ++.
Такие языки, как PERL, Java, JavaScript и C #, используют встроенные механизмы безопасности, которые минимизируют вероятность переполнения буфера.
Как предотвратить переполнение буфера
Разработчики могут защитить от уязвимостей переполнения буфера с помощью мер безопасности в своем коде или с помощью языков, которые предлагают встроенную защиту.
Кроме того, современные операционные системы имеют защиту во время выполнения. Три общих защиты:
- Рандомизация адресного пространства (ASLR) — случайным образом перемещается по местоположениям адресного пространства областей данных.Обычно для атак переполнения буфера необходимо знать местонахождение исполняемого кода, а рандомизация адресных пространств делает это практически невозможным.
- Предотвращение выполнения данных — помечает определенные области памяти как неисполняемые или исполняемые, что предотвращает запуск кода атаки в неисполняемой области.
- Защита от перезаписи структурированного обработчика исключений (SEHOP). — помогает предотвратить атаки вредоносного кода на Структурированную обработку исключений (SEH), встроенную систему для управления аппаратными и программными исключениями.Таким образом, это не позволяет злоумышленнику использовать технику эксплуатации перезаписи SEH. На функциональном уровне перезапись SEH достигается с помощью переполнения буфера на основе стека для перезаписи регистрационной записи исключения, хранящейся в стеке потока.
Меры безопасности в коде и защите операционной системы недостаточно. Когда организация обнаруживает уязвимость переполнения буфера, она должна быстро отреагировать, чтобы исправить уязвимое программное обеспечение и убедиться, что пользователи программного обеспечения могут получить доступ к исправлению.
Узнайте, как Imperva DDoS Protection может помочь вам с атаками переполнения буфера.
Как Imperva помогает смягчить атаки переполнения буфера
Решение безопасности Imperva развертывается как шлюз к вашему приложению и обеспечивает готовую защиту от атак переполнения буфера. Это достигается путем блокировки незаконных запросов, которые могут вызвать состояние переполнения буфера, не позволяя им достичь ваших приложений.
В дополнение к защите от атак переполнения буфера Imperva обеспечивает многоуровневую защиту, гарантирующую, что веб-сайты и приложения доступны, легко доступны и безопасны.В состав решения по обеспечению безопасности приложений Imperva входят:
- Защита от DDoS-атак — поддержание работоспособности в любых ситуациях. Предотвратите любые типы DDoS-атак любого размера, препятствующие доступу к вашему веб-сайту и сетевой инфраструктуре.
- Брандмауэр веб-приложений — разрешить законный трафик и предотвратить плохой трафик. Защитите свои приложения локально и на периферии с помощью облачного WAF корпоративного класса.
- Управление ботами — получите полную видимость и контроль над трафиком людей, хороших и плохих ботов на ваш сайт и API.
- Защита от захвата учетных записей — использует процесс обнаружения на основе намерений для выявления и защиты от попыток захвата учетных записей пользователей в злонамеренных целях.
- API Security — защищает API, гарантируя, что только желаемый трафик может получить доступ к вашей конечной точке API, а также обнаруживает и блокирует эксплойты уязвимостей.
- RASP — защитите свои приложения изнутри от известных атак и атак нулевого дня. Быстрая и точная защита без подписи или режима обучения.
- Аналитика атак — эффективное и точное устранение реальных угроз безопасности и реагирование на них с помощью действенной аналитики на всех уровнях защиты.
Что такое переполнение буфера? — Определение с сайта WhatIs.com
Переполнение буфера происходит, когда программа или процесс пытается записать в блок памяти или буфер фиксированной длины больше данных, чем выделено для хранения буфера. Поскольку буферы создаются для хранения определенного количества данных, дополнительные данные могут перезаписывать значения данных в адресах памяти, смежных с целевым буфером, если программа не включает достаточную проверку границ, чтобы отмечать или отбрасывать данные, когда слишком много отправляется в буфер памяти.
Использование переполнения буфера позволяет злоумышленнику контролировать процесс, прекращать его работу или изменять его внутренние переменные. Переполнение буфера всегда занимает высокое место в списке Common Weakness Enumeration / SANS Top 25 самых опасных программных ошибок и указано как CWE-120 в словаре Common Weakness Enumeration для типов уязвимостей. Несмотря на то, что это хорошо известно, переполнение буфера продолжает преследовать программное обеспечение как крупных, так и мелких производителей.
Переполнение буфера может произойти непреднамеренно, но оно также может быть вызвано тем, что злоумышленник отправляет тщательно созданный ввод в программу, которая затем пытается сохранить ввод в буфере, который недостаточно велик для этого ввода.Если избыточные данные записываются в соседний буфер, он перезаписывает любые данные, хранящиеся в нем. Если исходные данные включают указатель возврата эксплуатируемой функции — адрес, по которому процесс должен идти дальше, — злоумышленник может установить новые значения, указывающие на адрес по своему выбору. Злоумышленник обычно устанавливает новые значения так, чтобы они указывали на место, где была размещена полезная нагрузка эксплойта. Это изменяет путь выполнения процесса и эффективно передает управление вредоносному коду злоумышленника.
Языки программирования, такие как C и C ++, подвержены атакам переполнения буфера, поскольку у них нет встроенной защиты от доступа или перезаписи данных в любой части своей памяти, и поскольку субъекты могут выполнять прямые манипуляции с памятью с помощью общих программных конструкций. Современные языки программирования, такие как C #, Java и Perl, снижают вероятность ошибок кодирования, создающих уязвимости переполнения буфера, но переполнение буфера может существовать в любой среде программирования, где разрешено прямое манипулирование памятью, будь то из-за недостатков в компиляторе программы, библиотеках времени выполнения или особенностях сам язык.
Методы использования уязвимостей переполнения буфера различаются в зависимости от операционной системы и языка программирования, но цель всегда состоит в том, чтобы манипулировать памятью компьютера, чтобы подорвать или контролировать выполнение программы. Переполнение буфера классифицируется в соответствии с расположением буфера в памяти процесса, причем двумя основными типами являются переполнение на основе стека и переполнение на основе кучи.
Стек — это непрерывное пространство в памяти, используемое для организации данных, связанных с вызовами функций, включая параметры функций, локальные переменные функции и информацию управления, такую как указатели кадров и инструкций.
Куча — это структура памяти, используемая для управления динамической памятью. Программисты часто используют кучу для выделения памяти, размер которой неизвестен во время компиляции, где требуемый объем памяти слишком велик для размещения в стеке или где память предназначена для использования между вызовами функций.
Другие атаки, связанные с буфером, включают целочисленное переполнение, когда число используется в операции, результат которой требует больше памяти для хранения. Например, для хранения числа 192 требуется 8 бит памяти.Если процесс прибавит к этому числу 64, ответ 256 не поместится в выделенной памяти, так как для этого требуется 9 бит.
Атаки на форматирование строк изменяют поток приложения за счет использования функций библиотеки форматирования строк, таких как printf и sprintf, для доступа к другому пространству памяти.
Наконец, переполнение Unicode использует больший объем памяти, необходимый для хранения строки в формате Unicode, чем в символах ASCII.
Наиболее частая причина, по которой работают атаки переполнения буфера, заключается в том, что приложения не могут управлять выделением памяти и проверять ввод от клиента или других процессов.Приложения, разработанные на C или C ++, должны избегать опасных стандартных библиотечных функций, для которых не проверены границы, таких как gets, scanf и strcpy, и вместо этого использовать библиотеки или классы, явно созданные для безопасного выполнения строковых и других операций с памятью. Пользовательский ввод и данные из ненадежных источников всегда должны проверяться, чтобы гарантировать, что они находятся в пределах ожидаемых, и для предотвращения слишком длинных вводимых значений.
Поставщики
выпускают исправления и обновления для своего программного обеспечения, чтобы исправить обнаруженные уязвимости переполнения буфера, но все еще существует период риска между обнаружением уязвимости и созданием и развертыванием исправления.
В большинстве операционных систем предусмотрены средства защиты во время выполнения, чтобы затруднить успешную атаку переполнения буфера. Рандомизация разметки адресного пространства произвольно упорядочивает позиции адресного пространства областей ключевых данных процесса, включая основание исполняемого файла и позиции стека, кучи и библиотек. Это затрудняет для злоумышленника надежный переход к определенной функции в памяти.
Data Execution Prevention помечает области памяти как исполняемые или неисполняемые.Это предотвращает возможность злоумышленника выполнять инструкции, записанные в область данных через переполнение буфера.
Что такое переполнение буфера? Как хакеры используют эти уязвимости
Определение переполнения буфера
Переполнение или переполнение буфера — это проблема безопасности памяти, когда программа неправильно проверяет границы выделенного буфера памяти фиксированной длины и записывает больше данных, чем может вместить. Это приводит к переполнению данных в соседнее пространство памяти, перезаписывая там информацию, что часто приводит к сбоям и условиям эксплуатации.
Переполнение буфера — одна из старейших и наиболее частых причин уязвимостей выполнения произвольного кода, а приложения, написанные на таких языках программирования, как C и C ++, более подвержены таким ошибкам кодирования, чем другие языки. Чтобы избежать их, сообщество разработчиков разработало методы безопасного кодирования, и основные поставщики программного обеспечения приняли их как часть своих жизненных циклов безопасной разработки.
Растет число уязвимостей переполнения буфера
В сентябре 2019 года корпорация MITER, которая ведет каталог Common Weakness Enumeration (CWE), опубликовала список из 25 основных типов уязвимостей программного обеспечения.Наивысший ранг занял CWE-119 или «Неправильное ограничение операций в границах буфера памяти», более крупный класс ошибок обработки буфера, который включает переполнение буфера и чтение вне пределов.
Тот факт, что переполнение буфера по-прежнему занимает первое место после многих лет усилий по устранению его из компьютерного программного обеспечения, несколько удивляет. Тем не менее, это первый раз, когда MITER обновил список 25 основных слабых мест с 2011 года, и рейтинг основан на новой формуле оценки, которая объединяет частоту уязвимостей в Национальной базе данных уязвимостей (NVD), наблюдавшуюся в течение 2017 и 2018 годов, с их средними показателями серьезности. .Таким образом, список отражает общий риск, связанный с определенными типами слабостей, на основе как распространенности, так и опасности, которую они представляют.
Еще одна причина, по которой переполнение буфера снова в центре внимания, может быть связано с ростом числа устройств Интернета вещей (IoT), которые, согласно исследованиям, проведенным за последние несколько лет, показали низкое качество кода по сравнению с современными настольными приложениями известных производителей. продавцы. Прошивка встроенных систем исторически была пронизана проблемами переполнения буфера, и с годами ситуация не улучшилась, потому что эти неясные кодовые базы обычно не претерпевают серьезных изменений.Что изменилось, так это растущее количество таких устройств в Интернете, в деловых сетях и внутри домашних хозяйств.
Примеры атак переполнения буфера
Переполнения буфера обычно имеют высокий рейтинг серьезности, поскольку они могут привести к несанкционированному выполнению кода в случаях, когда злоумышленники могут контролировать перезаписываемое пространство памяти за пределами целевого буфера и могут перенаправить указатель функции на свой вредоносный код.
Даже когда выполнение произвольного кода невозможно, переполнение буфера часто приводит к сбою, ведущему к состоянию отказа в обслуживании (DoS), которое влияет на доступность приложения и обрабатываемых им процессов.Это особенно плохо для развертываний серверов, где требуется и ожидается постоянная доступность.
В определенных случаях злоумышленники могут также использовать переполнение буфера для перезаписи критических параметров в памяти приложения, например, флаг, указывающий, является ли пользователь администратором или нет. Это может привести к повышению привилегий в контексте приложения и, возможно, самой системы.
Наконец, неправильная обработка границ буфера может позволить злоумышленникам читать данные вне буфера вместо их перезаписи, что приводит к раскрытию конфиденциальной информации.Это называется чтением за пределами поля. Хотя это и отличается от классического переполнения буфера, чтение за пределами диапазона попадает в ту же категорию ошибок кодирования.
Чтения за пределами границы также могут использоваться для получения информации, которая может помочь злоумышленникам использовать другие уязвимости. Например, они могут использоваться для раскрытия адресов памяти, которые защищены технологиями защиты от использования ядра, такими как рандомизация разметки адресного пространства (ASLR).
Как предотвратить переполнение буфера
Поскольку переполнение буфера является результатом ошибок программирования, лучший способ предотвратить их — обучить разработчиков избегать таких ошибок.Многие руководства и книги по безопасному кодированию обращаются к переполнению буфера, как и программы сертификации по безопасному кодированию. Координационный центр CERT и Институт программной инженерии Университета Карнеги-Меллона разработали стандарты кодирования для нескольких языков программирования, включая C и C ++.
Автоматические способы предотвращения переполнения буфера включают использование безопасных для памяти языков программирования или фреймворков и библиотек, которые предоставляют безопасные версии функций, которые могут вызывать переполнение буфера.Разработчики также должны создавать приложения, которые используют такие функции, как ASLR и позиционно-независимые исполняемые файлы (PIE), чтобы ограничить потенциальное влияние переполнения буфера. Им также следует использовать флаги компилятора и расширения, которые обнаруживают такие ошибки, как флаг / GS в Microsoft Visual Studio, флаг FORTIFY_SOURCE GCC, добавленный Red Hat, StackGuard и ProPolice.
К сожалению, ни одно из этих решений не обеспечивает полную защиту, поэтому важно проводить периодические проверки кода и тестирование безопасности приложений с участием как внутренних, так и внешних групп безопасности, а также интегрировать такие инструменты, как фаззеры, в рабочие процессы автоматизированного тестирования.
Авторские права © 2020 IDG Communications, Inc.
Переполнение буфера — ENISA
Что такое «переполнение буфера»?
Переполнение буфера — это ошибка в компьютерной программе, которая может привести к уязвимости системы безопасности. Буфер — это часть физической памяти, которая временно используется для хранения данных. Переполнение буфера происходит, когда программа или процесс пытается записать или прочитать из буфера больше данных, чем может вместить буфер.
В контексте безопасности переполнение буфера может дать злоумышленнику доступ к различным частям внутренней памяти и, в конечном итоге, контролировать выполнение программы, создавая риски для конфиденциальности, целостности и доступности. Только программы с машинным кодом уязвимы для переполнения буфера, то есть программы, которые используют набор инструкций процесса напрямую, а не промежуточные звенья, такие как виртуальная машина Java. Эта уязвимость часто встречается в программах, написанных на языках программирования C или C ++, поскольку эти языки предназначены для обеспечения доступа к памяти.
Уязвимость переполнения буфера
Существуют различные методы использования уязвимости переполнения буфера, которые различаются в зависимости от архитектуры процессора, операционной системы (ОС) и области памяти. Пример уязвимой функции C — strcpy. Strcpy копирует строку, указанную источником, в массив, указанный в качестве назначения «strcpy (destination, source)», без проверки длины источника. Поэтому, если в целевой буфер помещаются большие объемы данных, это приведет к перезаписи памяти.Практический пример переполнения буфера с использованием функции strcpy:
char buff [5] = {0}
strcpy (buff, «Это больше, чем может вместить буфер»)
В приведенном выше примере мы сначала определяем массив типа char и размера 10. Затем мы используем функцию strcpy для записи строки в указанный массив большего размера, чем он может содержать, что в конечном итоге приводит к переполнению буфера. Эквивалентная команда, которая смягчает эту проблему, — strncpy, которая также принимает аргумент размера источника strncpy (destination, source, srcsize).
Одной из самых последних и критических ошибок, вызванных переполнением буфера, была «Stagefright», ошибка в библиотеке stagefright ОС Android. В этом случае злоумышленник смог удаленно скомпрометировать любой мобильный телефон под управлением ОС Android версии 2.2+, просто отправив искаженное видео, которое вызвало бы переполнение буфера в библиотеке stagefright и позволило выполнить вредоносный код.
Профилактика
Переполнение буфера происходит в программах, в которых им раскрыта структура памяти.Один из простых способов избежать переполнения буфера — использовать языки программирования с типобезопасностью, хотя эксплуатация по-прежнему возможна с помощью системных вызовов. Если существует абсолютная необходимость использовать язык программирования, не безопасный по типу, программисту важно быть осведомленным о безопасности, воздерживаться от использования общих уязвимых функций и проводить аудит кода перед запуском в производство.
Бит No-eXecute (NX)
Бит NX — это функция процессора, которая позволяет помечать определенные страницы памяти как неисполняемые.Таким образом, сегменты памяти, которые используются для данных, помечаются как доступные для чтения или записи. Процессор предотвратит выполнение кода из этих частей памяти.
Рандомизация разметки адресного пространства (ASLR)
ASLR — это метод рандомизации положения стека, исполняемых файлов и загруженных библиотек, что затрудняет злоумышленникам использование уязвимостей переполнения буфера.
Фузинг
Нечеткое или нечеткое тестирование — это метод, используемый для предоставления неверных или неожиданных данных для выполнения программы.Этот метод обычно выполняется с помощью автоматизированных инструментов, которые снабжают выполнение программы образцами данных. В этом случае фаззинг переполнения буфера может выявить ошибки в исходном коде, которые могут привести к необычному поведению программы. Это распространенный метод аудита исходного кода, позволяющий упреждающе проверять выполнение программы на наличие необработанных ошибок.
зависит от ОС
Современные ОС имеют свои собственные меры безопасности для ограничения воздействия уязвимостей переполнения буфера на систему.В Windows есть «Предотвращение выполнения данных (DEP)», в ОС Linux grsecurity и selinux и в Mac OS — «Обязательный контроль доступа (MAC)», предназначенный для предотвращения переполнения буфера или уязвимости системы.
Список литературы
Buffer Overflows: Attacks and Defence for the Vulnerability of the Decade, Криспин Коуэн, Перри Уэгл и др. Информационная безопасность: принципы и практика, Марк Стэмп
http://arstechnica.com/security/2015/08/how-security-flaws-work-the-buffer-overflow/
http: // c2.com / cgi / wiki? CeeLanguageAndBufferOverflows
http://insecure.org/stf/smashstack.html
https://www.owasp.org/index.php/Buffer_Overflows
http://projects.webappsec.org/w/ страница / 13246916 / Buffer% 20Overflow
http://www.cse.scu.edu/~tschwarz/coen152_05/Lectures/BufferOverflow.html
http://www.microsoft.com/security/sir/strategy/default.aspx #! section_3_3
https://pax.grsecurity.net/docs/aslr.txt
https://blog.zimperium.com/stagefright-vulnerability-details-stagefright-detector-tool-released/
Что такое переполнение буфера? Определение и ответы на часто задаваемые вопросы
<< Вернуться к техническому глоссарию
Определение переполнения буфера
Когда система записывает в буфер больше данных, чем она может вместить, происходит переполнение или переполнение буфера.Отсутствие надлежащей проверки вызывает эту программную уязвимость или ошибку, позволяющую записывать данные вне пределов. Это приводит к избыточным или потерянным данным и записи в соседнюю память, перезаписывая все, что было там ранее, и вызывает непредсказуемые эффекты.
Ошибка переполнения буфера делает систему уязвимой для злоумышленников, которые могут воспользоваться ею, внедрив специально адаптированный код. Этот вид вредоносного кода вызывает проблемы с переполнением буфера в сетевой безопасности и помещает исполняемый код в области памяти, смежные с переполнением.Этот последний код позволяет злоумышленнику запускать другие программы или получать доступ администратора.
Часто задаваемые вопросы
Что такое переполнение буфера?
Буфер — это последовательное выделение памяти или область, которая может содержать что угодно, от целочисленных массивов до символьных строк. Буферная область предназначена для хранения данных программы или приложения во время их перемещения из одной программы в другую или между разделами программы.
Переполнение буфера происходит, когда программа либо пытается поместить данные в область памяти за буфером, либо пытается поместить в буфер больше данных, чем он может вместить.Запись данных за пределы выделенного блока памяти может привести к сбою программы, повреждению данных или позволить злоумышленнику выполнить вредоносный код.
Неправильные входные данные — входные данные неправильного размера по дизайну — могут вызвать переполнение. Это возможно, потому что во многих случаях дизайнеры предполагают, что все входные данные будут меньше порогового размера, и создают буфер, соответствующий этому размеру. В этих ситуациях некоторые аномальные транзакции могут записывать за край буфера, производя больше данных.
Переполнение буфера — одна из наиболее серьезных уязвимостей программного обеспечения, которую могут использовать злоумышленники.Это связано с тем, что обнаружение и устранение переполнения буфера затруднено, особенно когда программное обеспечение очень сложное. Иногда исправления ошибок переполнения буфера сами по себе чреваты ошибками и сложны. И даже в тех случаях, когда программное обеспечение исправлялось много раз, многие риски безопасности, связанные с переполнением буфера, могут сохраняться.
Что такое атака переполнения буфера?
Хотя большинство разработчиков знакомы с атаками переполнения буфера и являются одними из наиболее распространенных уязвимостей безопасности программного обеспечения, они остаются обычным явлением как для недавно разработанных, так и для устаревших приложений.Отчасти это связано с тем, что существует множество способов использования уязвимости переполнения буфера, а отчасти потому, что существует множество способов предотвратить атаки переполнения буфера, которые могут привести к ошибкам.
Распределение памяти и структура многих систем четко определены, и эту четкую организацию легко использовать. Буферы — одна из таких распространенных функций в коде операционной системы (ОС). Перезапись известных областей посредством переполнения может позволить злоумышленнику захватить привилегии или заменить исполняемый код вредоносным кодом.
Например, еще в 1988 году червь Морриса использовал методы атаки переполнения буфера. Более свежие примеры включают атаки переполнения буфера против игровых сообществ, таких как Steam, которые приводят к сообщениям об ошибках «исходящее надежное переполнение буфера» для пользователей.
Языки программирования C и C ++ часто ассоциируются с пониманием переполнения буфера по нескольким причинам. В них отсутствует встроенная защита от доступа к данным в любом месте памяти, не говоря уже о перезаписи данных или исходного кода.Они также не могут автоматически проверять, находятся ли данные, записанные в массив, такой как буфер, в его пределах.
Проверка границ возможна в C и C ++, но требует дополнительного времени обработки и кода для предотвращения переполнения буфера. Более безопасные операционные системы применяют ряд стратегий для смягчения последствий переполнения буфера, таких как рандомизация компоновки пространства или намеренное создание пространства между целевыми буферами и запись в них действий, называемых канарейками или стеком канареек, для более эффективного мониторинга проблемы.
В классическом примере злоумышленник отправляет данные программы. Программа сохраняет данные в буфере стека меньшего размера, что приводит к перезаписи данных стека вызовов. Затем, когда функция возвращается, данные устанавливают значение указателя возврата и передают управление вредоносному коду злоумышленника.
Такой тип переполнения буфера стека распространен среди некоторых сообществ разработчиков и на определенных платформах. Однако существуют и другие разновидности эксплойтов, такие как единичная ошибка, переполнение буфера кучи и атака схожей строки формата.
Подробнее о типах тактик эксплойтов см. Ниже.
Примеры переполнения буфера
Большинство примеров атак переполнения буфера используют уязвимости, которые являются результатом предположений программистов. Тактика использования переполнения буфера часто основана на ошибочных предположениях о том, что такое данные и насколько велики их фрагменты, в сочетании с манипуляциями с ячейками системной памяти.
Обычно код с уязвимостями переполнения буфера:
- Зависит от свойств данных, которые применяются вне непосредственной области действия кода
- Для управления его поведением полагается на внешние данные, такие как ввод пользователя,
- Слишком сложно, чтобы программисты могли предсказать его поведение точно
Более пристальный взгляд на наиболее распространенные типы атак переполнения буфера:
Переполнение буфера стека
Стек похож на место в памяти операционной системы для выполнения функций, учета и локальных переменных.Система резервирует блок на вершине стека при вызове функции, и когда он возвращается, этот блок зарезервирован для повторного использования при следующем вызове функции.
Переполнение буфера стека, также называемое эксплуатацией на основе стека, позволяет злоумышленникам манипулировать системой несколькими способами:
- Перезапись локальной переменной рядом с уязвимым буфером стека для изменения поведения программы
- Перезапись адреса возврата кадра стека для возобновления выполнение после возврата функции, обычно в заполненном пользователем буфере, указанном злоумышленником вместо адреса возврата
- Перезапись и последующее выполнение обработчика исключения или возврата функции
- Перезапись локального указателя или переменной другого кадра стека на будет использоваться функцией в этом кадре позже
Обеспечение непредсказуемости адреса данных, предоставленного пользователем, затрудняет использование уязвимости переполнения буфера стека и удаленное выполнение кода.Чтобы обойти эту меру безопасности, некоторые злоумышленники используют трамплинги, метод, позволяющий им вычислить местоположение своего шелл-кода относительно указателя, который они идентифицируют рядом с уязвимым буфером стека. Затем они могут использовать инструкции, уже находящиеся в памяти, для перехода выполнения в шеллкод.
Переполнение буфера кучи
Куча — это системная память, предназначенная для динамического распределения. Она отличается от стековой памяти тем, что в ней нет шаблона «последний пришел — первый ушел» (LIFO), а не то, как блоки распределяются в куче.В куче вы можете динамически выделять или освобождать блок в любое время, а это означает, что отслеживать, какие части свободны или используются в любое время, гораздо сложнее.
Эксплуатация на основе кучи по-разному использует область данных кучи системной памяти.
Обычно система выделяет динамическую память, которая обычно включает в себя программные данные, динамически приложением во время выполнения. Злоумышленник стремится повредить эти данные, заставляя приложение перезаписывать указатели связанных списков и другие внутренние структуры.
Как предотвратить переполнение буфера
Существуют передовые методы предотвращения переполнения буфера, которые помогут защитить вашу систему:
Оцените уязвимость переполнения буфера. Во-первых, определите, уязвимы ли вы. Будьте в курсе последних отчетов об ошибках для всех библиотек и серверных продуктов. Просмотрите весь код пользовательского программного обеспечения, который принимает ввод данных пользователем через HTTP-запрос, чтобы убедиться, что он может правильно обрабатывать необычно большие вводимые данные. Это очень важно для обнаружения переполнения буфера.
Оставайтесь в курсе. Защитите свою системную инфраструктуру, обновляя отчеты об ошибках и применяя исправления. Используйте сканер переполнения буфера, чтобы контролировать свои сайты и приложения на предмет угроз безопасности. Затем подумайте, какие дополнительные тактики предотвращения атак из-за переполнения буфера — каждая из которых имеет свои преимущества и недостатки — могут сработать для вашей системы и организации.
Выбор языка. Надежное решение для переполнения буфера начинается на уровне языка, избегая уязвимых языков программирования.Однако это не решение для устаревшего кода, и бизнес, технические и другие параметры часто требуют использования уязвимого языка. Например, частично из-за того, что они не строго типизированы и допускают прямой доступ к памяти, C / C ++ уязвимы для атаки переполнения буфера в сетевой безопасности. Однако они остаются популярными языками программирования.
Сам
C не имеет встроенной защиты от переполнения буфера, потому что он не проверяет, находятся ли данные, записанные в буфер, в его пределах.C ++ ведет себя так же без явной проверки границ, хотя есть способы безопасной буферизации данных в стандартных библиотеках C ++ и контейнерах, которые могут дополнительно проверять границы в стандартной библиотеке шаблонов C ++ (STL) — если программист требует проверки границ при доступе данные явно.
Примеры строго типизированных языков, не допускающих прямого доступа к памяти, включают COBOL, Java и Python.
Некоторые языки программирования обеспечивают проверку во время компиляции или во время выполнения.Эти функции могут вызывать исключение или выдавать предупреждение в условиях, которые могут привести к сбою программы C или C ++. Эти языки включают Ada, Cyclone, D, Eiffel, Lisp, Modula-2, Ocaml, Rust и Smalltalk.
Большинство интерпретируемых языков будут защищать систему, сигнализируя четко определенные условия ошибки, такие как чтение, последовательное соединение и переполнение кольцевого буфера для некоторых конфигураций системы.
Безопасные библиотеки. Использование безопасных библиотек — еще одна тактика предотвращения переполнения буфера.Стандартные библиотечные функции для языков C и C ++ подвергают вашу систему переполнению, поскольку для них не проверяются границы. Хорошо протестированные и написанные библиотеки централизованно управляют буфером и выполняют его автоматически, включая проверку границ.
Массивы и строки — это два основных типа строительных блоков данных, уязвимых для переполнения буфера. Поэтому более безопасные библиотеки сосредоточены на этих типах данных для предотвращения атак. Часто встречающиеся примеры функций, которых следует избегать, — это strcpy (), scanf () и gets ().
Решения для переполнения буфера. Эти решения обнаруживают наиболее распространенные атаки, гарантируя, что стек остается неизменным после возврата из функции. Когда инструмент переполнения буфера обнаруживает изменение, он выходит из программы с ошибкой сегментации.
Деление стопки. Разделение стека на разделы для данных и функций также может обеспечить более надежную защиту стека. Однако это неполное решение проблемы переполнения буфера, поскольку оно защищает адрес возврата, но может допускать перезапись других конфиденциальных данных.
Защита указателя. Защита указателя — еще один аспект предотвращения переполнения буфера. Переполнение буфера влияет на указатели, включая адреса, хранящиеся в системе. Добавление кода к этим адресам защищает эти указатели, затрудняя надежное управление ими.
Исполняемая космическая защита. Защищая пространство исполняемых файлов, система вызывает исключение, если любой злоумышленник попытается выполнить код, который они вставляют либо в кучу, либо в стек.Таким образом, защита исполняемого пространства или предотвращение выполнения данных не устраняет исходные уязвимости, а вместо этого предотвращает выполнение кода переполнения буфера, если он присутствует. Это также означает, что он остается уязвимым для атак, которые не зависят от исполнения.
Даже когда злоумышленники не могут выполнить произвольный код, переполнение буфера часто вызывает сбой. Затем это приводит к отказу в обслуживании (DoS), который влияет на доступность приложений и процессов.
Рандомизация разметки адресного пространства (ASLR). Рандомизация компоновки адресного пространства распределяет важные области данных случайным образом в адресном пространстве процесса. Этот тип рандомизации адресов виртуальной памяти определяет местонахождение переменных и функций, чтобы сделать использование переполнения буфера более трудным, хотя и возможным. Этот подход также останавливает интернет-червей, вынуждая злоумышленников адаптировать свои попытки эксплуатации к каждой отдельной системе.
Глубокая проверка пакетов (DPI) или сканирование пакетов. Этот метод обнаруживает элементарные удаленные попытки использовать переполнение буфера на периметре сети.Этот метод блокирует NOP-салазки, длинные серии инструкций без операции и пакеты, которые имеют сигнатуры известных атак с использованием эвристики атак и сигнатур атак.
Однако DPI или сканирование пакетов — это базовый и менее эффективный метод, который может работать только против известных атак. Этот метод также уязвим для кодирования.
Инструменты тестирования переполнения буфера. Эти инструменты различаются, но их общая цель — обнаружение переполнения буфера, выявление их причин и исправление этих слабых мест.Граничное тестирование, фаззинг и статический анализ — все это методы автоматического тестирования переполнения буфера.
Предлагает ли Avi защиту от переполнения?
Платформа Avi служит шлюзом для вашего приложения, обеспечивая готовое решение для защиты от атак переполнения буфера. Avi предотвращает попадание незаконных запросов в ваши приложения, не позволяя им запускать состояние переполнения буфера. Платформа также предлагает многоуровневую защиту приложений и веб-сайтов.
Интеллектуальный брандмауэр веб-приложений Avi Network (iWAF) обеспечивает высокопроизводительную безопасность веб-приложений.iWAF использует каталог известных угроз Common Vulnerabilities and Exposures (CVE). Этот каталог ведется Министерством внутренней безопасности с целью стандартизации определения эксплойтов для упрощения реакции администраторов. Эксплойты, использующие уязвимости переполнения буфера, включены в CVE и предотвращены Avi’s iWAF.
Определение атак переполнения буфера и способы защиты от них
Во время переполнения буфера программа помещает данные в область памяти вне буфера.Этот шаг может повредить ваши данные или привести к сбою вашей программы.
Хакеры знают это и иногда используют ваш кажущийся невинным буфер, чтобы захватить ваше устройство.
По сути, буфер — это спаситель, если одной из ваших программ не хватает памяти и она не хочет аварийно завершиться. Программа может хранить данные в этом буфере и продолжать работу. Когда вашей программе требуется больше памяти, чем она может найти в буфере, она уязвима для атаки переполнения буфера.
Атаки переполнения буфера стали одними из самых печально известных примеров взлома, от червя Морриса в 1998 году до Stagefright в 2015 году.
Давайте объясним, как они работают, и мы поможем вам понять, как от них защититься.
Что такое переполнение буфера?
Практически на каждом компьютере есть буфер. В этом выделенном пространстве могут храниться или переноситься данные, поэтому вы можете продолжать работать без многократных сбоев. Но даже у самого лучшего буфера есть предел. И когда вы его превышаете, начинается переполнение.
Переполнение буфера может быть:
- Случайно. Вы пытаетесь сделать слишком много в рамках одной программы, и вы превышаете пространство, которое программа предоставляет вам.Программа может начать работать хаотично, а в некоторых случаях вообще перестать работать.
- Умышленное. Кто-то отправляет данные, которые слишком велики для вашей программы. Этот набор данных содержит код, который может заменить действительные версии. Когда новый код запускается, он может заставить программу делать то, чего вы не ожидаете.
Хакеру может показаться трудным создать программу, которая полностью понимает вашу программу и может изменить способ ее работы. Но, к сожалению, эти атаки довольно часты.
Типы атак переполнения буфера
Каждая программа содержит буфер, но злоумышленник может воспользоваться одним из двух методов, чтобы захватить его и начать атаку.
Атака переполнения буфера может быть:
- На основе стека. Злоумышленник отправляет данные программе, и эта передача сохраняется в слишком маленьком стековом буфере. Ваш хакер может выбрать функцию «push» и хранить новые элементы в верхней части стека. Или хакер может выбрать функцию «поп», удалить верхний элемент и заменить его.Это означает, что хакер официально внедрил вредоносный код и взял под свой контроль.
- На основе кучи. Ваш хакер повреждает данные в куче, и это изменение кода вынуждает вашу систему перезаписывать важные данные.
Как пользователь, вы можете не видеть этих изменений. Но если вы не остановите прогресс, ваша программа может начать тормозить. Со временем он может рухнуть.
Поскольку переполнение буфера часто нацелено на важные программы, такие как операционные системы, взлом может оставить вас с устройством, которым вы не можете управлять.
Знайте свои уязвимости, связанные с переполнением буфера
Предотвратить атаку легче, чем оправиться от нее. К счастью, вы можете сделать множество вещей, чтобы обезопасить свою компанию.
Начните с изучения кода, который вы используете во время разработки. Языки программирования, уязвимые для атак переполнения буфера, включают:
Эти языки не имеют встроенной защиты и не допускают рутинных проверок на переполнение.
Затем проверьте свой код.Хакеру проще запустить атаку, когда ваши программы:
- Сложный. Трудно ли вам предсказать поведение вашей программы?
- Внешний. Вам нужны сторонние или сторонние данные для управления действиями в программе? Контролируются ли свойства данных извне?
- Старше. Вы использовали устаревшие компоненты для создания программы?
Практически в каждой компании есть старая программа, созданная на заказ, которую каждый сотрудник использует каждый день.Возможно, вы с подозрением относитесь к замене этой системы, поскольку она была создана специально для вас. Но очевидно, что устаревшие системы сопряжены с рисками, которые сложно уменьшить или устранить.
План предотвращения и предотвращения переполнения буфера
Хакеры годами опирались на буферы, но появляются новые возможности для их работы. Например, эксперты говорят, что подключенные устройства (включая элементы Интернета вещей, такие как холодильники и дверные звонки) могут быть уязвимы для этих атак.
Защитите свою компанию, следуя здравому смыслу:
- Используйте новые операционные системы. Пора отказаться от устаревших программ с просроченными системами поддержки. Более новый код имеет больше защиты.
- Следите за языком. Программы, написанные на COBOL, Python и Java, вероятно, безопаснее других.
- Добавьте место. Некоторые программы допускают защиту исполняемого пространства. Если этот параметр включен, хакер не может выполнить код, вставленный с помощью атаки переполнения.
- Положитесь на разработчиков. Системные администраторы часто жалуются, что разработчики игнорируют их отчеты об ошибках. Будьте настойчивы. Когда вы обнаружите проблему, ведущую к переполнению буфера, продолжайте говорить о ней, пока кто-нибудь ее не исправит.
- Примените свои патчи. Когда разработчики узнают о проблемах с переполнением буфера, они исправляют их с помощью кода. Например, в феврале 2021 года разработчики Sudo выпустили такой же патч, и они призвали людей использовать его.
Наблюдение за своим кодом, анализ поведения и частое тестирование — все это хорошие идеи.Но чтобы ваша компания была в безопасности, вам нужно сделать еще больше.
Работайте с Okta, чтобы окружить ваши самые важные ресурсы надежной системой аутентификации. Узнайте больше о том, как мы можем помочь таким организациям, как ваша.
Список литературы
Атака переполнения буфера. Комплексная безопасность: практический подход.
Переполнение буфера. OWASP.
Атака переполнения буфера. Университет Санта-Клары.
Что такое переполнение буфера? И как хакеры используют эти уязвимости.(Январь 2020 г.). ОГО.
Ядро: игнорируют ли разработчики Linux сообщения об ошибках? (Сентябрь 2007 г.). Networkworld.
Уязвимость Sudo, связанная с переполнением буфера. (Февраль 2021 г.). Агентство по кибербезопасности и безопасности инфраструктуры.
Атаки переполнения буфера
Атаки переполнения буфера
Переполнение буфера происходит, когда программа пытается записать слишком много данных в
блок памяти фиксированной длины (буфер). Переполнение буфера может использоваться
злоумышленники могут вывести из строя веб-сервер или выполнить вредоносный код. Если ваш веб-сервер
уязвим для атак переполнения буфера, это только вопрос времени, когда
хакер внедряет код и берет под свой контроль вашу систему.
Риски
Переполнение буфера в C и C ++
Переполнение буфера в C и C ++ возникает, когда вы используете небезопасные функции, которые не
проверить длину данных, записываемых в буфер. Если вы пишете код C или C ++,
обязательно используйте следующие безопасные эквивалентные функции:
Небезопасная функция | Безопасная альтернатива |
---|---|
получает () | fgets () |
strcpy () | strncpy () |
strcat () | strncat () |
sprintf () | snprintf () |
Переполнение буфера в приложениях, которые вы используете
Веб-разработчики довольно редко пишут низкоуровневый код на таких языках, как C
или C ++, поэтому самый большой риск переполнения буфера для нас в
приложения, которые мы используем.
Веб-серверы
Большинство веб-сайтов развертываются с использованием веб-сервера для обслуживания статического контента. (Этот
отличается от сервера приложений , который выполняет динамическое содержимое.)
три самых распространенных веб-сервера:
- HTTP-сервер Apache
- Microsoft Internet Information Services (IIS)
- Nginx
Было обнаружено, что каждый из них уязвим для переполнения буфера на разных
раз. Производители веб-серверов очень быстро исправляют уязвимости, поэтому ключ
чтобы обезопасить себя, — это установка исправлений безопасности, как только они станут
имеется в наличии.
Операционные системы и время выполнения
Атаки переполнения буфера были запущены против веб-сайтов, воспользовавшись преимуществом
уязвимостей в операционных системах и языковых средах. Heartbleed
атака использовала серьезную уязвимость в криптографической системе OpenSSL.
программная библиотека, которую веб-серверы на базе Linux используют для шифрования трафика SSL / TLS.
Точно так же исследователи безопасности обнаружили уязвимости в различных
функции во время выполнения PHP, которые позволяют злоумышленникам запускать переполнение буфера
атакует удаленно, создавая вредоносный ввод.
Восстановление
Чтобы избежать уязвимостей переполнения буфера в приложениях
вы используете, вам необходимо поддерживать их в актуальном состоянии с помощью последних исправлений безопасности.
Вот ключевые вещи, которые необходимо сделать:
Автоматизируйте процесс сборки и развертывания. Вам нужно знать, какой
версии каждого приложения, которое вы запускаете на каждом сервере. Это означает
написание сценариев развертывания для веб-серверов и языковых сред, а также
сохранение копий журналов развертывания.Будьте в курсе бюллетеней по безопасности. Убедитесь, что ваша команда начеку
для объявлений о безопасности используемых вами приложений. Записаться на рассылку
списки, присоединяйтесь к форумам и следите за поставщиками программного обеспечения в социальных сетях.Размещайте исправления безопасности, как только они станут доступны! Хакеры будут
найти способы воспользоваться уязвимостями безопасности, как только они
обнародованы, поэтому убедитесь, что вы не входите в целевую аудиторию.