Уязвимость в браузере opera (переполнение буфера)

Большие переменные в стеке

Третья большая причина переполнения стека — одноразовое выделение огромного количества памяти крупными локальными переменными. Многие авторы рекомендуют выделять память, превышающую несколько килобайт, в «куче», а не на стеке.

Пример на Си:

int foo() {
     double x1000000];
}

Массив занимает 8 мегабайт памяти; если в стеке нет такого количества памяти, случится переполнение.

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

Уязвимости компиляции

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

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

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

Он работает путем рандомизации областей памяти структур, так что их смещения сложнее определить. Если бы эта защита существовала в конце 1980-х годов, червя Морриса можно было бы не допустить. Это связано с тем, что он функционировал частично, заполняя буфер в протоколе UNIX finger кодом эксплойта, а затем переполнял его, чтобы изменить адрес возврата и указывал на заполненный буфер.

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

Примеры из жизни

Исследование SPECCINT

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

Результаты анализа SPECCINT2000 показывают наличие 219 статических источников переполнения в 8 из 12 бенчмарков, из которых 148 использовали беззнаковое переполнение и 71 — знаковое (снова неопределённое поведение). В то же время, беззнаковое переполнение тоже не всегда намеренное и может являться ошибкой и источником уязвимости (например, Listing 2 той же статьи).

Также был проведён тест на «бомбы замедленного действия» в SPECCINT2006. Его идеей является в каждом месте неопределённого поведения вернуть случайное число и посмотреть, к каким последствиям это может привести. Если оценивать неопределённое поведение с точки зрения стандарта C99/C++11, то тест не пройдут целых 6 бенчмарков из 9.

Примеры из других программных пакетов

1 int  addsi (int lhs , int  rhs) {
2     errno = ;
3     if (((( lhs+rhs)^lhs)&(( lhs+rhs)^rhs))
4     >> (sizeof(int)*CHAR_BIT -1)) {
5         error_handler("OVERFLOW  ERROR", NULL , EOVERFLOW);
6         errno = EINVAL;
7     }
8     return  lhs+rhs;
9 }

Данный фрагмент кода из пакета IntegerLib проверяет, могут ли быть lhs и rhs сложены без переполнения. И ровно в строчке 3 это переполнение может возникнуть (при сложении lhs+rhs). Это UB, так как lhs и rhs — знакового типа. Кроме этого, в данной библиотеке найдено ещё 19 UB-переполнений.

Также авторы сообщили разработчикам о 13 переполнениях в SQLite, 43 в SafeInt, 6 в GNU MPC library, 30 в PHP, 18 в Firefox, 71 в GCC, 29 в PostgreSQL, 5 в LLVM и 28 в Python. Большинство из ошибок были вскоре исправлены.

Другие примеры

Такая же проблема появляется в игре Civilization 4 и известна как Nuclear Gandhi. Дело в том, что после заключения партнёрских соглашений с очень миролюбивым Ганди, происходит переполнение через 0 уровня враждебности, результатом чего может стать ядерная война с Ганди.

Ещё одним примером является глюк в SimCity 2000. Здесь дело в том, что бюджет игрока стал очень большим, а после перехода через 231 внезапно стал отрицательным. Игра оканчивается поражением.

Этот глюк из Diablo III. Из-за одного из изменений патча 1.0.8, игровая экономика сломалась. Максимальную сумму для сделок повысили с 1 млн до 10 млн. Стоимость покупки переполнялась через 32-битный тип, а при отмене операции возвращалась полная сумма. То есть игрок оставался с прибылью в 232 игровой валюты

Безопасность[править]

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

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

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

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

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

Многие языки программирования, например, Java и Lisp, управляют выделением памяти автоматически, и используют комбинацию статического анализа и проверки корректности действий программы во время выполнения. Это делает ошибки, связанные с переполнением буфера, маловероятными или невозможными. Perl для избежания переполнений буфера обеспечивает автоматическое изменение размера массивов. Однако, системы времени выполнения и библиотеки для таких языков всё равно могут быть подвержены переполнениям буфера, вследствие возможных внутренних ошибок в реализации этих систем проверки. В Windows доступны некоторые программные решения, которые предотвращают выполнение кода за пределами переполненного буфера, если такое переполнение было осуществлено. Среди этих решений — DEP в Windows XP SP2, OSsurance и Anti-Execute.

Определено значение переполнения буфера! ошибка?

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

Это отклонение от правильности и точности. Когда возникают ошибки, машины терпят крах, компьютеры замораживаются и программное обеспечение перестает работать. Ошибки — это в основном непреднамеренные события. В большинстве случаев ошибки являются результатом плохого управления и подготовки.

Эксплуатация и последствия

Основные последствия для безопасности:

  • Для доступности. Возможный отказ системы (окно для DoS-атак)
  • Для целостности. Неавторизированный доступ к данным
  • Для конфиденциальности. Исполнение стороннего кода, обход защитного механизма

Классически переполнение может быть эксплуатировано через переполнение буфера.

img_t table_ptr; /*struct containing img data, 10kB each*/
int num_imgs;
...
num_imgs = get_num_imgs();
table_ptr = (img_t*)malloc(sizeof(img_t)*num_imgs);
...

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

Другая уязвимость в том, что если num_imgs ещё больше, это приведёт к переполнению аргумента malloc. Тогда выделится лишь небольшой буфер

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

Глава III

(Основная цель : Как разрешить ситуацию с плохими опкодами) (Вторичная цель : Написание «полезной нагрузки»)

Основная цель : Как разрешить ситуацию с плохими опкодами

Мы перенаправили адрес возврата на наш код, но он наш код должен быть неприкосновенен, чтобы успешно выполнить свою задачу в дальнейшем… так как он может быть неприкосновенен? Во время переполнения буфера код передается API- или иной процедуре. И что? Переполнения буфера часто происходят во время обработки строк, которые копируют/перемещают байты строки, пока не доходят до NULL. Некоторые API также останавливаются на байтах CR или LF (0dh, 0ah). Если ваш код содержит какие-либо байты NULL, что бывает всегда (хорошо, почти всегда), вам придется зашифровать его. Лучший метод — это комбинировать XOR и ADD, с помощью которых можно сделать зашифрованный код, в котором почти не будет символов NULL/CR/LF.

Так как код декриптора не может содержать NULL-байтов, мы должны быть находчивы как это только возможно. Чтобы получить смещения, по которым находятся NULL-байты, мы должны использовать то, как CALL помещает адрес возврата на стек и POPит его в регистр. Например:

Наш зашифрованный код находится в памяти, так же рак и его декриптор. Что дальше? «Полезная нагрузка»… Нам нужен какой-то код, который будет выполнен. Давайте дадим волю воображению.

Вторичная цель : Написание «полезной нагрузки»

Теперь, когда контроль в ваших руках, и ваш код может выполниться, то чего вы ждете? Теперь можно, например, открыть соединение с интернетом и скачать дополнительный компонент, это называется EGG-процедура. Вы также можете открыть backdoor, а если вы находитесь на машине, отключенной от интернета, вы можете выполнить какой-нибудь бат-файл, с командами, которые должны быть исполнены с более высокими правами. Если вы находитесь на NT-машине, вы можете запустить командную строку (CMD.EXE), которая запустится на более высоком уровне уровне доступа, как и все, что вы будете делать с ее помощью. Я не буду объяснять, как получить API-адрес. Вы можете достать их из KERNEL32.DLL тем же методом, который применяется в win32-вирусах. Вы можете достать их из таблицы импорта программы, к которой был применен эксплойт, но иногда в них нет всех необходимых API. Тогда вам нужно посмотреть LoadLibrary и GetProcAddress. Я оставляю это на вас.

Asmodeus iKX, пер. Aquila

Источник WASM.RU

Эксплуатация

Техники применения переполнения буфера меняются в зависимости от архитектуры, операционной системы и области памяти. Например, случай с переполнением буфера в куче (используемой для динамического выделения памяти) значительно отличается от аналогичного в стеке вызовов.

Эксплуатация в стеке

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

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

Если адрес пользовательских данных неизвестен, но он хранится в регистре, можно применить метод «trampolining» (с англ. — «прыжки на батуте»): адрес возврата может быть перезаписан адресом опкода, который передаст управление в область памяти с пользовательскими данными. Если адрес хранится в регистре R, то переход к команде, передающей управление по этому адресу (например, call R), вызовет исполнение заданного пользователем кода. Адреса подходящих опкодов или байтов памяти могут быть найдены в DLL или в самом исполняемом файле. Однако адреса обычно не могут содержать нулевых символов, а местонахождения этих опкодов меняются в зависимости от приложения и операционной системы. Metasploit Project, например, хранил базу данных подходящих опкодов для систем Windows (на данный момент она недоступна).

Переполнение буфера в стеке не нужно путать с переполнением стека.

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

Эксплуатация в куче

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

Уязвимость в продукте GDI+ компании Microsoft, возникающая при обработке изображений формата JPEG — пример опасности, которую может представлять переполнение буфера в куче.

Сложности в эксплуатации

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

Риски для безопасности

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

Такой вид некорректности в коде ведёт к следующим последствиям:

  1. Компиляция может пойти неожиданным образом. Из-за наличия неопределённого поведения в программе, оптимизации компилятора могут изменить поведение программы.
  2. Бомба замедленного действия. На текущей версии ОС, компилятора, опций компиляции, структурной организации программы и т. д. всё может работать, а при любом изменении, например, появлении более агрессивных оптимизаций, сломаться.
  3. Иллюзия предсказуемости. Конкретная конфигурация компилятора может иметь вполне определённое поведение, например компиляторы языков C и C++ обычно реализуют операции по модулю 2n и для знаковых типов (только интерпретированных в дополнительном коде), если отключены агрессивные оптимизации. Однако, надеяться на такое поведение нельзя, иначе есть риск эффекта «бомбы замедленного действия»
  4. Образование диалектов. Некоторые компиляторы предоставляют дополнительные опции для того, чтобы доопределить неопределённое поведение. Например, и GCC, и Clang поддерживают опцию -fwrapv, обеспечивающую выше описанное (в пункте 3) поведение.

Изменение стандарта может привести к новым проблемам с переполнением. К примеру, 1<<31 было зависимым от реализации в стандартах ANSI C и C++98, в то время как стали неопределённым в C99 and C11 (для 32-битных целых).

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

Обнаружены причины переполнения буфера! ошибка?

Если вы получили эту ошибку на своем ПК, это означает, что произошла сбой в работе вашей системы. Общие причины включают неправильную или неудачную установку или удаление программного обеспечения, которое может привести к недействительным записям в вашем реестре Windows, последствиям атаки вирусов или вредоносных программ, неправильному отключению системы из-за сбоя питания или другого фактора, кто-то с небольшими техническими знаниями, случайно удалив необходимый системный файл или запись в реестре, а также ряд других причин. Непосредственной причиной ошибки «Ошибка переполнения буфера» является неспособность правильно выполнить одну из своих нормальных операций с помощью системного или прикладного компонента.

Краткое техническое изложение[править]

Описаниеправить

Рассмотрим более подробно случай переполнения буфера, расположенного в области стека. Это удобнее всего сделать с помощью примера программы на языке Си или C++. Пример ориентирован на архитектуру x86, но работает и на многих других.

Когда динамический буфер, представляющий собой автоматический массив, выделяется в функции, он создаётся на стеке во время вызова этой функции. В архитектуре x86 стек растёт от бо́льших адресов к меньшим (или справа налево, в приведённых ниже диаграммах), то есть новые данные помещаются перед теми, которые уже находятся в стеке. Здесь, представляет существующий стек, и — это некоторое новое значение, которое ЦП поместил в стек:

(NEWDATA)(DATA)(DATA)(…)

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

(ADDR)(DATA)(DATA)(…)

Когда выделятся динамический буфер, стек растёт влево на размер буфера. Так, если функция начинается с объявления , результатом будет:

(.a………)(ADDR)(DATA)(DATA)(…)

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

Предположим, что 10-байтный буфер предназначен для того, чтобы содержать данные, предоставляемые пользователем (например — пароль). Если программа не проверяет количество символов, которые были введены пользователем, и записывает 14 байт в буфер, эти лишние данные «лягут» поверх адреса возврата, перезаписывая его новыми данными. Таким образом, это изменит место, в которое будет передано управление, когда завершится подпрограмма, и с которого программа продолжит исполнение после этого.

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

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

Примерправить

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

 /* overflow.c - демонстрирует процесс переполнения буфера */

 #include <stdio.h>
 #include <string.h>
 
 int main(int argc, char *argv[])
 {
   char buffer10];
   if (argc < 2)
   {
     fprintf(stderr, "ИСПОЛЬЗОВАНИЕ: %s строка\n", argv]);
     return 1;
   }
   strcpy(buffer, argv1]);
   return ;
 }

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

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

 /* better.c - демонстрирует, как исправить ошибку */
 
 #include <stdio.h>
 #include <string.h>
 #define BUFFER_SIZE 10
 
 int main(int argc, char *argv[])
 {
   char bufferBUFFER_SIZE];
   if (argc < 2)
   {
     fprintf(stderr, "ИСПОЛЬЗОВАНИЕ: %s строка\n", argv]);
     return 1;
   }
   strncpy(buffer, argv1], BUFFER_SIZE - 1);
   bufferBUFFER_SIZE - 1 = '\0';
   return ;
 }

Примеры

В качестве примера рассмотрим код для рекурсивного поиска файлов, расположенный на MSDN:

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

Пример второго подхода, взятый из вопроса «Почему происходит переполнение стека?» с сайта под названием Stack Overflow (сайт является сборником вопросов и ответов на любые программистские темы, а не только по переполнению стека, как может показаться):

Как видно, в функции main выделяется память в стеке под массивы типов int и float по миллиону элементов каждый, что в сумме дает чуть менее 8 мегабайт. Если учесть, что по умолчанию Visual C++ резервирует под стек лишь 1 мегабайт, то ответ становится очевидным.

А вот пример, взятый из GitHub-репозитория проекта Flash-плеера Lightspark:

Можно надеятся, что h.getLength()-7 не будет слишком большим числом, чтобы на следующей строчке не произошло переполнения. Но стоит ли сэкономленное на выделении памяти время «потенциального» вылета программы?

Методы безопасной разработки

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

Облачная служба Veracode выявляет уязвимости кода, такие как переполнение buffer, поэтому разработчики устраняют их до того, как они будут использованы. Уникальная в отрасли запатентованная технология тестирования безопасности бинарных статических приложений (SAST) Veracode анализирует его, включая компоненты с открытым исходным кодом и сторонние, без необходимости доступа к нему.

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

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

Уязвимость переполнения buffer существует уже почти 3 десятилетия, но она по-прежнему обременительна. Хакеры по всему миру продолжают считать ее своей тактикой по умолчанию из-за огромного количества восприимчивых веб-приложений. Разработчики и программисты затрачивают огромные усилия для борьбы с этим злом IT-технологий, придумывая все новые и новые способы.

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

Предотвращение проблемы

Защита от подобного поведения должна проводиться на нескольких уровнях:

  1. Планирования и требований к программе:
    • Убедитесь, что все протоколы взаимодействия между компонентами строго определены. В том числе, что все вычисления вне границ представления будут обнаружены. И требуйте строгого соответствия этим протоколам
    • Используйте язык программирования и компилятор, которые не позволяет этой уязвимости воплотиться, либо позволяют легче её обнаружить, либо выполняют авто-проверку границ. Инструменты, которые предоставляются компилятором, включают санитайзеры (например, Address Sanitizer или Undefined Behavior Sanitizer) .
  2. Архитектуры программы:
    • Используйте проверенные библиотеки или фреймворки, помогающие проводить вычисления без риска непредсказуемых последствий. Примеры включают такие библиотеки, как SafeInt (C++) или IntegerLib (C or C++).
  3. Реализации:
    • Проводите валидацию любых поступивших извне числовых данных, проверяя что они находятся внутри ожидаемого диапазона. Проверяйте обязательно как минимальный порог, так и максимальный. Используйте беззнаковые числа, где это возможно. Это упростит проверки на переполнение.
    • Подробно изучите полученные от компилятора предупреждения и устраните возможные проблемы безопасности, такие как несоответствия знаковости операндов при операциях с памятью или использование неинициализированных переменных. Даже если уязвимость совсем небольшая, это может привести к опасности для всей системы.

Другие правила, позволяющие избежать этих уязвимостей, опубликованы в стандарте CERT C Secure Coding Standard в 2008 году, включают:

  • Не пишите и не используйте функции обработки строкового ввода, если они обрабатывают не все случаи
  • Не используйте битовые операции над знаковыми типами
  • Вычисляйте выражения на типе большего размера перед сравнением или присваиванием в меньший
  • Будьте внимательны перед приведением типа между числом и указателем
  • Убедитесь, что вычисления по модулю или результаты деления не приводят к последующему делению на ноль
  • Используйте intmax_t или uintmax_t для форматированного ввода-вывода пользовательских числовых типов
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector