Ошибки программирования
Наибольшую опасность с точки зрения безопасности представляют ошибки в модулях, связанных с проверкой ACL, авторизацией и повышением уровня привилегий процессора (например, в диспетчере системных вызовов), а в системах семейства Unix – в setuid-программах.
Одна из наиболее опасных – и в то же время довольно распространенная в современных программах (пример 1).
Пример 1. Пример программы, подверженной срыву стека
/* Фрагмент примитивной реализации сервера SMTP (RFC822) */
int parse_line(FILE * socket)
{
/* Согласно RFC822, команда имеет длину не более 4 байт,
а вся строка — не более 255 байт */
char cmd[5], args[255];
fscanf(socket, "%s %s\n", and, args);
/* Остаток программы нас не интересует */
Видно, что программа считывает из сетевого соединения строку, которая должна состоять из двух полей, разделенных пробелом, и заканчиваться символом перевода строки. В соответствии со спецификациями протокола SMTP, первое поле (команда) не может превышать четырех символов (к сожалению, не определено в протоколе более длинных команд), а строка целиком не может быть длиннее 255 байт.
Если партнер на другом конце соединения полностью соответствует требованиям [RFC 0822], наш код будет работать без проблем. Проблемы – причем серьезнейшие – возникнут, если будет передана строка, которая этим требованиям не соответствует.
Превышение допустимой длины кодом команды не представляет большой опасности: лишние байты будут записаны в начало массива args и потеряны при записи в него его собственного поля. Настоящая опасность – это превышение длины всей строки. Для партнера не представляет никаких сложностей сгенерировать последовательность из более чем 255 символов, не содержащую переводов строки (рис. 7).
Рис. 7. Срыв буфера
Тогда буфер arg переполнится, но за ним в памяти следует вовсе не другой буфер, а – ни много, ни мало, заголовок стекового кадра, в котором содержится адрес возврата подпрограммы.
Переполнение массива arg приведет к нарушению заголовка стекового кадра. Если диверсант достаточно квалифицирован, он может передать вместо команды кусок кода и поддельный стековый кадр, который в качестве адреса возврата содержит адрес переданного кода. И тогда, при попытке возвратить управление, программа передаст управление на подставленный ей код (рис. 8).
Даже если вредитель не может передать и исполнить код (например, потому, что не знает адреса стека или потому, что стек защищен от исполнения), порчи стекового кадра достаточно, чтобы аварийно завершить исполнение сетевого сервиса, а это тоже неприятно. Ошибки такого рода называются переполнениями буфера (buffer overrun) или срывами буфера. Если буфер находится в стеке, говорят еще о срыве стека. Срывы буфера возможны не только в сетевых сервисах, но и в приложениях, просто считывающих файлы и, с другой стороны, не только в высокоуровневых сетевых сервисах, но и в драйверах сетевых протоколов нижнего уровня – последний тип ошибок особенно опасен, потому что атаке подвергается модуль ядра ОС. Особенную опасность представляет срыв буфера в модулях, осуществляющих парольную авторизацию: в этом случае злоумышленник может даже но разрушать стековый кадр, ему достаточно лишь модифицировать переменную, которая сигнализирует, что пароль успешно проверен.
Рис. 8. Срыв стека с передачей троянского кода
Наиболее велика опасность срыва буфера в ситуациях, когда спецификация сетевого протокола или формата файла гласит, что длина того или иного поля или пакета не может превышать определенного количества байтов, однако нарушение этого правила физически возможно. Возможность такого нарушения может возникать как из-за того, что используется не счетчик байтов в пакете, а маркер конца пакета, так и из-за того, что разрядность счетчика байтов позволяет представлять значения, превышающие установленный протоколом предел.
При программировании на языке С основные источники ошибок такого рода – это использование стандартных процедур gets и fscanf. Процедура gets лечению не подлежит – ей невозможно указать размер буфера, выделенного для приема данных, поэтому она в принципе не способна проконтролировать его заполнение. Вместо нее современные версии библиотек С предоставляют функцию fgets, которой размер буфера передается в качестве параметра. Настоятельно рекомендуется использовать именно ее.
Процедура fscanf в данном случае лечится. Вместо
fscanf(socket, "%s %s\n", cmd, args);
следует написать
fscanf(socket, "%4s %255s\n", cmd, args);
Однако автоматизировать проверку того, что в каждом случае все форматные спецификаторы указаны правильно, невозможно, и поэтому использовать процедуру fscanf и другие процедуры того же семейства не рекомендуется.
В тесном концептуальном родстве со срывами буфера находятся ошибки, срабатывающие при использовании во входном потоке данных недопустимых величин смещения (рис. 9). Такие ошибки встречаются при анализе входного потока, который содержит взаимосвязанные структуры данных, связи между которыми реализованы в виде смешений в потоке. Практически важный пример такого протокола – система квитирования (посылки подтверждений) со скользящим окном, используемая в транспортном протоколе TCP [RFC 0793]. Адресация посредством смещений широко применяется также при работе с последовательными файлами, поэтому драйверы файловых систем и сетевые файловые серверы также могут содержать такие ошибки.
Рис. 9. Использование недопустимых смещений
Задание смещений, превосходящих размер буфера анализирующей программы, или недопустимых по каким-либо другим правилам – например, отрицательных, если по протоколу допустимы только положительные – может приводить к формированию указателей за пределы анализируемого буфера. Модификация данных по этим указателям может приводить к разнообразным последствиям – например, таким способом можно попытаться убедить файловый сервер, что файл, открытый для чтения, в действительности открыт для записи.
В многопоточных сервисах распространены также ошибки соревнования. Для их срабатывания необходима определенная последовательность и временное согласование запросов к сервису.
В некоторых типах сервисов встречаются свойственные им ошибки. Так, во многих серверах HTTP была обнаружена ошибка подъема по каталогам (directory traversal bug), когда злоумышленник, запрашивая URL, содержащие последовательности '..', мог подняться по файловой системе выше корневого каталога HTTP-сервера и, таким образом, считать или даже модифицировать файлы, не входящие в иерархию HTML-документов (рис. 10). Аналогичные ошибки встречаются и в сетевых файловых серверах.
Рис. 10. Ошибка подъема по каталогам
Общим правилом, позволяющим если не искоренить ошибки такого рода, то во всяком случае, уменьшить вероятность их совершения, является недоверие к входным потокам данных. Если спецификации протокола или формата гласят что то или иное условие обязано выполняться, мы не можем просто считать что оно выполняется, а обязаны как минимум вставить явную проверку того, что оно выполняется. Программа тестирования программного комплекса должна включать не только проверку правильной обработки допустимых входных данных в каждом из модулей, осмысленную реакцию на недопустимые входные данные.
Дата добавления: 2015-04-01; просмотров: 862;