15.3. Виды программных уязвимостей

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

Червём называется самовоспроизводящаяся отдельная (под-)программа, которая может исполняться и распространяться самостоятельно, не используя программу-носитель.

Первой вехой в изучении компьютерных вирусов можно назвать 1949 год, когда Джон фон Нейман прочёл курс лекций в Университете Иллинойса под названием «Теория самовоспроизводящихся машин» (изданы в 1966 [80], переведены на русский язык издательством «Мир» в 1971 году [129]), в котором ввёл понятие самовоспроизводящихся механических машин. Первым сетевым вирусом считается вирус Creeper 1971 г., распространявшийся в сети ARPANET, предшественнице Интернета. Для его уничтожения был создан первый антивирус Reaper, который находил и уничтожал Creeper.

Первый червь для Интернета, червь Морриса, 1988 г., уже использовал смешанные атаки для заражения UNIX машин [34, 99]. Сначала программа получала доступ к удалённому запуску команд, эксплуатируя уязвимости в сервисах sendmail, finger (с использованием атаки на переполнение буфера) или rsh. Далее, с помощью механизма подбора паролей червь получал доступ к локальным аккаунтам пользователей:

Программной уязвимостью называется свойство программы, позволяющее нарушить её работу. Программные уязвимости могут приводить к отказу в обслуживании (Denial of Service, DoS-атака), утечке и изменению данных, появлению и распространению вирусов и червей.

Одной из распространённых атак для заражения персональных компьютеров является переполнение буфера в стеке. В интернет-сервисах наиболее распространённой программной уязвимостью в настоящее время является межсайтовый скриптинг (Cross-Site Scripting, XSS-атака).

Наиболее распространённые программные уязвимости можно разделить на классы:

  1. Переполнение буфера – копирование в буфер данных большего размера, чем длина выделенного буфера. Буфером может быть контейнер текстовой строки, массив, динамически выделяемая память и т. д. Переполнение становится возможным вследствие либо отсутствия контроля над длиной копируемых данных, либо из-за ошибок в коде. Типичная ошибка – разница в 1 байт между размерами буфера и данных при сравнении.
  2. Некорректная обработка (парсинг) данных, введённых пользователем, является причиной большинства программных уязвимостей в веб-приложениях. Под обработкой понимаются:
    1. проверка на допустимые значения и тип (числовые поля не должны содержать строки и т. д.);
    2. фильтрация и экранирование специальных символов, имеющих значения в скриптовых языках или применяющихся для перекодирования из одной текстовой кодировки в другую. Примеры символов: , , , , ", ';
    3. фильтрация ключевых слов языков разметки и скриптов. Примеры: script, JavaScript;
    4. перекодирование различными кодировками при парсинге. Распространённый способ обхода системы контроля парсинга данных состоит в однократном или множественном последовательном кодировании текстовых данных в шестнадцатеричные кодировки NN ASCII и UTF-8. Например, браузер или веб-приложения производят $n$-кратное перекодирование, в то время как система контроля делает $k$-кратное перекодирование, $0 \leq k < n$, и, следовательно, пропускает закодированные запрещённые символы и слова.
  3. Некорректное использование функций. Например, printf(s) может привести к уязвимости записи в память по указанному адресу. Если злоумышленник вместо обычной текстовой строки введёт в качестве s "текст некоторой длиныn", то функция printf, ожидающая первым аргументом строку формата fmt, обнаружив n, возьмёт значение из ячеек памяти, находящихся перед ячейками с указателем на текстовую строку (устройство стека описано далее), и запишет в память по адресу, равному считанному значению, количество выведенных символов на печать функцией printf.