Переполнение буфера передачи изображения finereader

Обновлено: 04.07.2024

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

Бывает, что переполнение буфера при программировании продукта является средством, служащим определённым целям, например, намеренно применяется системным софтом для обхода отдельных ограничений. Рассмотрим подробнее, что это за явление, почему возникает и как избавиться от системной ошибки.

Причины возникновения ошибки переполнения стекового буфера

Для размещения данных программами используются блоки памяти (буферы), обычно фиксированной длины, то есть вмещающие ограниченный объём информации. Ошибка переполнения стекового буфера возникает, когда приложение пишет больше данных, чем выделено под стековый буфер, провоцируя перезаписывание, и не исключено, что будут перезаписаны важные избыточные данные в стеке, расположенные следом за массивом или перед ним.

Причины возникновения ошибки переполнения стекового буфера

Стек (абстрактный тип данных) являет собой список элементов, располагающихся стопкой, где информация упорядочена таким образом, что добавление элемента делает его головным, а удаление убирает первый элемент, тогда как головным станет следующий за ним. Принцип работы стека часто сравнивается со стопкой тарелок – выдернуть из середины тарелку нельзя, снимаются они поочерёдно, начиная с верхней, то есть порядок взаимодействия осуществляется по принципу LIFO (Last In, First Out – последним пришёл, первым ушёл).

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

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

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

Что делать, если обнаружена уязвимость в данном приложении

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

Использование антивирусного ПО

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

Рекомендуется просканировать систему на вирусы, можно в безопасном режиме, если ОС не загружается, и выполнить проверку и устранение угроз посредством встроенного Защитника Windows.

Как очистить компьютер от вирусов при появлении ошибки «Стековый буфер переполнен»:

  • Открываем Защитник Windows через поисковую строку меню «Пуск» или в области уведомлений на панели задач;
  • Выбираем «Защита от вирусов и угроз» и переходим к параметрам сканирования;
  • Отмечаем флажком «Автономное сканирование Защитника Windows» и жмём соответствующую кнопку для начала проверки.

Чистая загрузка ОС Windows

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


Для чистой загрузки Windows выполняем следующие действия:

  1. Открываем консоль «Выполнить» (Win+R), вводим в поле команду msconfig, жмём «Ок» или клавишу Enter.
  2. В окне «Конфигурация системы» на вкладке «Общие» снимаем отметку с пункта «Загружать элементы автозагрузки». Затем на вкладке «Службы» отмечаем пункт «Не отображать службы Майкрософт» и жмём кнопку «Отключить все».
  3. Идём на вкладку «Автозагрузка» и жмём ссылку «Открыть диспетчер задач» (для Windows 10), в открывшемся окне Диспетчера задач поочерёдно отключаем каждую программу в списке.
  4. Возвращаемся к окну конфигурации и жмём «Ок», после чего перезагружаемся и проверяем, исчезла ли ошибка.

Для того чтобы выявить программу, ставшую причиной проблемы, нужно включать софт по одному в автозагрузке и службах, после чего выполнять перезагрузку.

Специализированный софт

Восстановление Windows

Ещё одна мера, позволяющая избавится от системной ошибки, предполагает выполнение восстановления системы. Для использования функции потребуется наличие заранее созданного накопителя восстановления Windows, в качестве которого можно использовать диск или флешку. Выполняем следующие действия:

  • отключаем от компьютера лишние устройства, не требуемые для работы;
  • вставляем загрузочный накопитель и загружаемся с него, предварительно выставив приоритет загрузки в BIOS;
  • выбираем «Восстановление системы» – «Диагностика» – «Дополнительные параметры» – «Восстановление при загрузке», далее выбираем ОС, которую требуется восстановить, и ждём окончания процесса, перезагружаемся.

Восстановление Windows


Даже если код написан на «безопасном» языке (например, на Python), если используются любые написанные на C, C++ или Objective C библиотеки, он все равно может быть уязвим для переполнения буфера.

Выделение памяти

Чтобы понять механизм возникновения переполнения буфера, нужно немного разобраться с выделением памяти в программы. В написанном на языке С приложении можно выделить память в стеке во время компиляции или в куче во время выполнения.

  • Объявление переменной в стеке: int numberPoints = 10 .
  • Объявление переменной в куче: int* ptr = malloc (10 * sizeof(int)) .

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

Поскольку куча реже хранит возвращаемые адреса, гораздо сложнее (хотя в ряде случаев это возможно) запустить эксплойт. Память в куче обычно содержит данные программы и динамически выделяется по мере ее выполнения. Это означает, что при переполнении кучи, скорее всего, перезапишется указатель функции – такой путь более сложен и менее эффективен чем переполнение стека.

Поскольку переполнение стека является наиболее часто используемым типом переполнения буфера, кратко рассмотрим, как именно они работают.

Переполнение стека

Эксплуатация уязвимости происходит внутри процесса, при этом каждый процесс имеет свой собственный стек. Когда он выполняет основную функцию, то находит как новые локальные переменные (которые будут «запушены» в начало стека), так и вызовы других функций (которые создадут новый «стекфрейм»).


Что такое stackframe?

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

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

  • Stack Pointer: указывает на топ стека вызовов процесса (или на последний помещенный в стек элемент).
  • Instruction Pointer: указывает на адрес следующей инструкции процессора, которая будет выполнена.
  • Base Pointer (BP): (также известный как указатель кадра) указывает на основание текущего кадра стека. Он остается постоянным до тех пор, пока программа выполняет текущий стекфрейм (хотя указатель стека может измениться).

Для примера рассмотрим следующий код:

Стек вызовов будет выглядеть следующим образом, сразу после вызова firstFunction и выполнения оператора int x = 1+z :


Здесь main вызывает firstFunction (которая в данный момент выполняется), поэтому она находится в верхней части стека вызовов. Возвращаемый адрес – это адрес в памяти, относящийся к функции, которая его вызвала (он удерживается указателем инструкции при создании стекфрейма). Локальные переменные, которые все еще находятся в области видимости, также находятся в стеке вызовов. Когда они выполняются и выходят за пределы области действия, они удаляются из верха стека.

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

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

Этот простой код считывает произвольное количество данных ( gets будет считывать до конца файла или символа новой строки). Рассмотрев его, можно понять опасность. Если пользователь вводит больше данных, чем помещается в выделенную для переменной область, введенная строка перезапишет следующие ячейки памяти в стеке вызовов. Если она достаточно длинная, перезапишется даже обратный адрес вызывающей функции.

Как компьютер отреагирует на это, зависит от реализации стеков и выделения памяти в конкретной системе. Реакция на переполнение буфера может быть совершенно непредсказуемой, начиная от сбоев программы и заканчивая выполнением вредоносного кода.

Почему происходит переполнение буфера?

Причина, по которой переполнение буфера стало такой серьезной проблемой, заключается в отсутствии проверки границ во многих функции управления памятью в C и C++. Хотя этот процесс сейчас довольно хорошо известен, он также очень часто эксплуатируется (например, зловред WannaCry использовал переполнение буфера).

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

Веб-серверы, серверные приложения и среды веб-приложений подвержены переполнению буфера. Исключение составляют написанные на интерпретируемых языках среды, хотя сами интерпретаторы тоже могут быть подвержены переполнению.

Как уменьшить влияние переполнения буфера:

  • Используйте интерпретируемый язык, который не подвержен этим проблемам.
  • Избегайте использования функций, которые не выполняют проверку буфера (например, в C вместо функции gets() используйте функцию fgets()).
  • Применяйте компиляторы, которые помогают определить небезопасные функции или найти ошибки.
  • Используйте canaries, которые могут помочь предотвратить переполнение буфера. Они вставляются перед обратным адресом в стеке и проверяются перед обращением к нему. Если программа обнаружит изменение значения canary, она прервет процесс, не позволив злоумышленнику пробиться. Значение canary является либо случайным (поэтому злоумышленнику очень трудно его угадать), либо строкой, которую по техническим причинам невозможно перезаписать.
  • Переставляйте локальные переменных таким образом, чтобы скалярные (отдельные объекты данных фиксированного размера) были выше переменных массива, содержащих несколько значений. Это означает, что если переменные массива переполняются, они не будут влиять на скалярные переменные. Этот метод в сочетании с canary-значениями очень помогает.
  • Сделайте стек неисполняемым, установив бит NX (No-eXecute), чтобы злоумышленник не вставлял шелл-код непосредственно в стек и не выполнял его там. Это неидеальное решение, так как даже неисполняемые стеки могут стать жертвами атак переполнения буфера, вроде return-to-libc. Эта атака происходит, когда обратный адрес стекового фрейма заменяется адресом библиотеки, уже находящейся в адресном пространстве процесса. К тому же не все процессоры позволяют установить бит NX.
  • ASLR (рандомизация расположения адресного пространства) может служить общей защитой, а также специфической защитой от атак return-to-libc. Это означает, что всякий раз, когда файл библиотеки или другая функция вызывается запущенным процессом, ее адрес сдвигается на случайное число. Это делает практически невозможным связать фиксированный адрес памяти процесса с функциями, из чего следует, что злоумышленнику может быть трудно узнать, откуда вызывать определенные функции. ASLR включен по умолчанию во многих версиях Linux, OS X и Android.

Stack Underflow

Такая уязвимость возникает, когда две части программы по-разному обрабатывают один и тот же блок памяти. Например, если вы выделите массив размером X, но заполните его массивом размером x < X, а затем попытаетесь извлечь все X байтов, скорее всего вы получите «грязные» данные для X – x байтов.

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

Заключение

Рассмотренная уязвимость является очень серьезной угрозой стабильной работе любого продукта. Необходимо приложить все усилия и проверить ваши проекты на ее наличие, т. к. последствия могут быть весьма плачевными (уже упоминался Ransome ) и болезненными. Используйте советы из статьи и вы уменьшите вероятность успешного проникновения злоумышленников в ваш код. Удачи в обучении!

Читайте также: